<template>
  <div class="jobs-container" :style="containerStyle" v-loading="loading">
    <!-- 页面标题和操作按钮 -->
    <div class="page-header">
      <div class="header-background">
        <div class="bg-pattern"></div>
        <div class="bg-gradient"></div>
      </div>
      
      <div class="page-title">
        <div class="title-icon">
          <div class="icon-container" style="width: 36px; height: 36px; background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%); border-radius: 8px; color: white; box-shadow: 0 2px 8px rgba(103, 194, 58, 0.2);">
            <el-icon size="20"><Folder /></el-icon>
            <div class="icon-glow"></div>
          </div>
        </div>
        <div class="title-content">
          <div class="title-main">
            <h1>作业管理</h1>
            <div class="title-badge">
              <el-icon><Star /></el-icon>
              <span>Data</span>
            </div>
          </div>
          <p class="page-description">
            <el-icon><Monitor /></el-icon>
            管理和监控Data作业的运行状态，实时掌握平台健康度
          </p>
          <div class="title-meta">
            <div class="meta-item">
              <el-icon><Clock /></el-icon>
              <span>实时监控</span>
            </div>
            <div class="meta-item">
              <el-icon><Lock /></el-icon>
              <span>自动恢复</span>
            </div>
            <div class="meta-item">
              <el-icon><Menu /></el-icon>
              <span>性能分析</span>
            </div>
          </div>
        </div>
      </div>
      
      <div class="page-stats">
        <div class="stat-card running">
          <div class="stat-icon">
            <el-icon><Right /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ getJobCountByStatus('RUNNING') }}</div>
            <div class="stat-label">运行中</div>
            <div class="stat-trend">
              <el-icon><Menu /></el-icon>
              <span>活跃</span>
            </div>
          </div>
        </div>
        
        <div class="stat-card failed">
          <div class="stat-icon">
            <el-icon><Warning /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ getJobCountByStatus('FAILED') }}</div>
            <div class="stat-label">失败</div>
            <div class="stat-trend">
              <el-icon><Warning /></el-icon>
              <span>需关注</span>
            </div>
          </div>
        </div>
        
        <div class="stat-card finished">
          <div class="stat-icon">
            <el-icon><Check /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ getJobCountByStatus('FINISHED') }}</div>
            <div class="stat-label">已完成</div>
            <div class="stat-trend">
              <el-icon><Check /></el-icon>
              <span>正常</span>
            </div>
          </div>
        </div>
        
        <div class="stat-card total">
          <div class="stat-icon">
            <el-icon><Menu /></el-icon>
          </div>
          <div class="stat-content">
            <div class="stat-number">{{ totalAll || total }}</div>
            <div class="stat-label">总计</div>
            <div class="stat-trend">
              <el-icon><Menu /></el-icon>
              <span>全部</span>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 搜索和筛选区域 -->
    <div class="search-section">
      <el-card shadow="hover" class="search-card">
        <template #header>
          <div class="search-header">
            <div class="search-title">
              <el-icon><Search /></el-icon>
              <span>搜索与筛选</span>
            </div>
          </div>
        </template>
        <el-form :model="searchForm" class="search-form">
          <div class="search-form-content">
            <div class="search-form-fields">
              <el-form-item label="作业名称">
                <el-input
                  v-model="searchForm.job_name"
                  placeholder="请输入作业名称"
                  clearable
                  style="width: 200px"
                />
              </el-form-item>
              
          <el-form-item label="状态">
            <el-select v-model="searchForm.status" placeholder="选择状态" clearable style="width: 120px">
              <el-option label="全部" value="" />
              <el-option label="RUNNING" value="RUNNING" />
              <el-option label="FINISHED" value="FINISHED" />
              <el-option label="FAILED" value="FAILED" />
              <el-option label="CANCELED" value="CANCELED" />
            </el-select>
          </el-form-item>
              
          <el-form-item label="监控状态">
            <el-select v-model="searchForm.monitor_status" placeholder="选择监控状态" clearable style="width: 160px">
              <el-option label="全部" value="" />
              <el-option label="监控中" value="ACTIVE" />
              <el-option label="已停止监控" value="DISMONITOR" />
              <el-option label="禁用恢复" value="DISRECOVER" />
              <el-option label="完全禁用" value="DISABLED" />
            </el-select>
          </el-form-item>
            </div>
            
            <div class="search-form-actions">
              <el-button type="primary" @click="handleSearch" class="search-btn">
              <el-icon><Search /></el-icon>
              搜索
            </el-button>
              <el-button type="warning" @click="handleReset" class="reset-btn">
              <el-icon><Refresh /></el-icon>
              重置
            </el-button>
              <el-button type="info" @click="showFieldSelector = !showFieldSelector" class="field-btn">
                <el-icon>
                  <component :is="showFieldSelector ? 'ArrowUp' : 'ArrowDown'" />
                </el-icon>
                字段设置
              </el-button>
              <el-button type="success" @click="openRegisterDialog">
                <el-icon><Plus /></el-icon>
                注册作业
              </el-button>
              <el-button type="primary" @click="showDiscoveryDialog = true" plain>
                <el-icon><Search /></el-icon>
                发现未注册作业
              </el-button>
              <el-button type="danger" @click="$router.push('/flink-monitor-config')" plain>
                <el-icon><Setting /></el-icon>
                系统配置
              </el-button>
              <el-button type="info" @click="$router.push('/restart-history')" plain>
                <el-icon><Refresh /></el-icon>
                重启历史
              </el-button>
            </div>
          </div>
        </el-form>
        
        <!-- 字段设置面板 -->
        <transition name="field-panel">
          <div v-if="showFieldSelector" class="field-selector-panel">
          <div class="field-sorter">
            <div class="sorter-header">
              <h4>字段排序设置</h4>
              <p class="sorter-description">使用上下箭头调整字段显示顺序，勾选要显示的字段</p>
            </div>
            <div class="field-list-sortable">
              <div
                v-for="(field, index) in sortedFields"
                :key="field.value"
                class="field-item"
                :class="{ 'field-disabled': !selectedFields.includes(field.value) }"
              >
                <div class="field-drag-handle">
                  <span class="drag-icon">⋮⋮</span>
                </div>
                <el-checkbox
                  v-model="selectedFields"
                  :value="field.value"
                  :disabled="field.required"
                  class="field-checkbox"
                  @click.stop
                >
                  <span class="field-label">{{ field.label }}</span>
                  <span v-if="field.required" class="required-mark">*</span>
                </el-checkbox>
                <div class="field-group-tag">
                  <el-tag size="small" :type="getGroupTagType(field.group)">
                    {{ getGroupTitle(field.group) }}
                  </el-tag>
                </div>
                <div class="field-sort-controls">
                  <el-button
                    size="small"
                    :disabled="index === 0"
                    @click="moveFieldUp(index)"
                    circle
                    type="primary"
                    plain
                  >
                    ↑
                  </el-button>
                  <el-button
                    size="small"
                    :disabled="index === sortedFields.length - 1"
                    @click="moveFieldDown(index)"
                    circle
                    type="primary"
                    plain
                  >
                    ↓
                  </el-button>
                </div>
              </div>
            </div>
          </div>
          
          <!-- 字段设置操作按钮 -->
          <div class="field-actions">
            <el-button type="primary" @click="saveCurrentFieldSettings">
              <el-icon><Check /></el-icon>
              保存设置
            </el-button>
            <el-button type="success" @click="setAsDefaultSettings">
              <el-icon><Star /></el-icon>
              设为默认
            </el-button>
            <el-button @click="resetFieldSettings">
              <el-icon><Refresh /></el-icon>
              重置默认
            </el-button>
            <el-button type="warning" @click="clearDefaultSettings">
              <el-icon><Delete /></el-icon>
              清除默认
            </el-button>
            <el-button @click="showFieldSelector = false">
              <el-icon><Close /></el-icon>
              关闭
            </el-button>
          </div>
          </div>
        </transition>
      </el-card>
    </div>

    <!-- 批量操作区域 -->
    <div class="batch-actions-bar" v-if="selectedJobs.length > 0">
      <div class="batch-info-compact">
        <el-icon class="selected-icon"><Select /></el-icon>
        <span>已选择<strong>{{ selectedJobs.length }}</strong>个作业</span>
      </div>
      <div class="batch-divider">|</div>
      <div class="batch-buttons-compact">
        <el-button type="warning" class="batch-btn-compact" @click="showBulkStopDialog = true">
          <el-icon><Remove /></el-icon>
          批量停止
        </el-button>
        <el-button type="success" class="batch-btn-compact" @click="showBulkRestartDialog = true">
          <el-icon><Refresh /></el-icon>
          批量重启
        </el-button>
        <el-button type="danger" class="batch-btn-compact" @click="showBulkDeleteDialog = true">
          <el-icon><Delete /></el-icon>
          批量删除
        </el-button>
        <el-button 
          class="batch-btn-compact monitor-btn"
          :type="allMonitorActive ? 'warning' : 'success'" 
          @click="handleToggleMonitor">
          <el-icon>
            <Remove v-if="allMonitorActive" />
            <VideoPlay v-else />
          </el-icon>
          {{ allMonitorActive ? '停止监控' : '开始监控' }}
        </el-button>
        <el-button 
          class="batch-btn-compact recovery-btn"
          :type="allRecoveryActive ? 'info' : 'primary'" 
          @click="handleToggleRecovery">
          <el-icon>
            <Close v-if="allRecoveryActive" />
            <Check v-else />
          </el-icon>
          {{ allRecoveryActive ? '停止自动恢复' : '开始自动恢复' }}
        </el-button>
        <el-button 
          class="batch-btn-compact alert-btn"
          :type="allAlertEnabled ? 'warning' : 'success'" 
          @click="handleToggleAlert">
          <el-icon>
            <BellFilled v-if="allAlertEnabled" />
            <Bell v-else />
          </el-icon>
          {{ allAlertEnabled ? '关闭告警' : '开启告警' }}
        </el-button>
      </div>
    </div>

    <!-- 作业列表 -->
    <div class="table-section" style="position: relative;">
      <el-card shadow="hover" class="table-card">
        <template #header>
          <div class="table-header">
            <div class="table-title">
              <el-icon><List /></el-icon>
              <span>作业列表</span>
            </div>
            <div class="table-header-actions">
              <el-button type="success" @click="handleRefresh" class="refresh-btn" :loading="loading" size="small">
                <el-icon><Refresh /></el-icon>
                刷新
              </el-button>
              <div class="table-info">
                <!-- 合并的分页信息控制 -->
                <div class="pagination-control-wrapper" @click="paginationExpanded = !paginationExpanded">
                  <span class="page-count">本页 {{ jobs.length }} 条</span>
                  <div class="divider"></div>
                  <span class="pagination-info">{{ currentPage }}/{{ Math.ceil(total / pageSize) }}</span>
                  <el-icon class="pagination-icon" :class="{ 'expanded': paginationExpanded }">
                    <ArrowDown />
                  </el-icon>
                </div>
              </div>
            </div>
          </div>
        </template>
        <div class="table-container">
            <el-table
              :data="jobs"
              @selection-change="handleSelectionChange"
              @sort-change="handleSortChange"
              :default-sort="{ prop: 'last_update_time', order: 'descending' }"
              style="width: 100%;"
              :row-key="(row, index) => row.job_name || row.job_id || index"
              :lazy="true"
              :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
              :height="600"
              :virtual-scrolling="false"
              :virtual-scroll-item-size="50"
              table-layout="fixed"
              :row-class-name="getRowClassName"
              class="enhanced-table"
            >
          <el-table-column type="selection" width="55" />
          
          
          <template v-for="field in sortedFields" :key="field.value">
          <el-table-column
              v-if="selectedFields.includes(field.value)"
              :prop="field.value"
              :label="getFieldDisplayName(field.value)"
              :width="getFieldWidth(field.value)"
              :min-width="getFieldMinWidth(field.value)"
              :show-overflow-tooltip="getFieldShowOverflow(field.value)"
              :sortable="field.value === 'last_update_time' ? 'custom' : false"
              :sort-orders="['descending', 'ascending', null]"
          >
              <template #default="{ row }">
                <!-- 作业名称字段 -->
                <div v-if="field.value === 'job_name'" class="job-name-cell">
                  <span class="job-name-text">{{ row.job_name }}</span>
                  <el-tooltip content="复制作业名称" placement="top">
                    <el-button 
                      type="info"
                      size="small" 
                      @click.stop="handleCopyJobName(row.job_name)" 
                      circle
                      class="copy-btn"
                    >
                      <el-icon><DocumentCopy /></el-icon>
                    </el-button>
                  </el-tooltip>
                </div>
                
                <!-- 状态字段 -->
                <div v-else-if="field.value === 'last_status'" class="status-container">
                  <el-tag :type="getStatusType(row.last_status)" size="small" class="status-tag">
                    <el-icon v-if="row.last_status === 'RUNNING'" class="status-icon"><Right /></el-icon>
                    <el-icon v-else-if="row.last_status === 'FAILED'" class="status-icon"><Warning /></el-icon>
                    <el-icon v-else-if="row.last_status === 'FINISHED'" class="status-icon"><Check /></el-icon>
                    <el-icon v-else-if="row.last_status === 'CANCELED'" class="status-icon"><Close /></el-icon>
                    <span class="status-text">{{ row.last_status || 'UNKNOWN' }}</span>
                </el-tag>
                  <div class="status-indicator" :class="`indicator-${row.last_status?.toLowerCase()}`"></div>
                </div>
                
                <!-- 自动恢复开关 -->
                <el-switch
                  v-else-if="field.value === 'auto_recovery'"
                  v-model="row.auto_recovery"
                  :active-value="1"
                  :inactive-value="0"
                  @change="(value) => handleToggleAutoRecovery(row, value)"
                  :disabled="!row.job_name"
                />
                
                <!-- 监控状态标签 -->
                <el-tag v-else-if="field.value === 'monitor_status'" :type="getMonitorStatusType(row.monitor_status)" size="small">
                  {{ getMonitorStatusText(row.monitor_status) }}
                </el-tag>
                
                <!-- 健康状态标签 -->
                <el-tag v-else-if="field.value === 'health_status'" :type="getHealthStatusType(row.health_status)" size="small">
                  {{ row.health_status || 'UNKNOWN' }}
                </el-tag>
                
                <!-- 是否监控开关 -->
                <el-switch
                  v-else-if="field.value === 'manual_stopped'"
                  v-model="row.manual_stopped"
                  :active-value="0"
                  :inactive-value="1"
                  @change="(value) => handleToggleManualStopped(row, value)"
                  :disabled="!row.job_name"
                />
                
                <!-- 告警开关 -->
                <el-switch
                  v-else-if="field.value === 'is_alert_enabled'"
                  v-model="row.is_alert_enabled"
                  :active-value="1"
                  :inactive-value="0"
                  @change="(value) => handleToggleAlertEnabled(row, value)"
                  :disabled="!row.job_name || row.manual_stopped === 1"
                />
                
                <!-- 最大恢复次数 - 带加减按钮 -->
                <div v-else-if="field.value === 'max_recovery_attempts'" class="number-control">
                  <el-button
                    size="small"
                    :disabled="!row.job_name || row.max_recovery_attempts <= 0"
                    @click="decreaseMaxRecoveryAttempts(row)"
                    circle
                    type="primary"
                    plain
                  >
                    -
                  </el-button>
                  <span class="number-value">{{ row.max_recovery_attempts || 0 }}</span>
                  <el-button
                    size="small"
                    :disabled="!row.job_name || row.max_recovery_attempts >= 100"
                    @click="increaseMaxRecoveryAttempts(row)"
                    circle
                    type="primary"
                    plain
                  >
                    +
                  </el-button>
                </div>
                
                <!-- 错误类型标签 -->
                <el-tag v-else-if="field.value === 'last_error_type'" :type="row.last_error_type ? 'danger' : 'info'" size="small">
                  {{ row.last_error_type || '-' }}
                </el-tag>
                
                <!-- 时间字段 -->
                <el-text v-else-if="['registration_time', 'last_update_time', 'last_running_time', 'last_stop_time', 
                                   'last_check_time', 'last_successful_check', 'last_health_check', 
                                   'last_savepoint_time', 'last_error_time'].includes(field.value)" 
                         size="small">
                  {{ formatTime(row[field.value]) }}
                </el-text>
                
                <!-- JAR名称标签 -->
                <div v-else-if="field.value === 'jar_name'" class="copyable-field" @click="copyToClipboard(row.jar_name, 'JAR名称')">
                  <el-tag size="small">{{ row.jar_name || '-' }}</el-tag>
                  <div class="copy-hint">
                    <el-icon><Document /></el-icon>
                  </div>
                </div>
                
                <!-- 连续失败次数 -->
                <el-text v-else-if="field.value === 'consecutive_failures'" 
                         size="small" 
                         :type="row.consecutive_failures > 0 ? 'danger' : 'success'">
                  {{ row.consecutive_failures || 0 }}
                </el-text>
                
                <!-- 恢复次数 -->
                <el-text v-else-if="field.value === 'recovery_attempts'" 
                         size="small" 
                         :type="row.recovery_attempts > 0 ? 'warning' : 'success'">
                  {{ row.recovery_attempts || 0 }}
                </el-text>
                
                <!-- 错误信息 -->
                <el-text v-else-if="field.value === 'error_message'" size="small" type="danger">
                  {{ row.error_message || '-' }}
                </el-text>
                
                <!-- 作业ID -->
                <div v-else-if="field.value === 'job_id'" class="copyable-field" @click="copyToClipboard(row.job_id, '作业ID')">
                  <el-text type="info" size="small">{{ row.job_id || '-' }}</el-text>
                  <div class="copy-hint">
                    <el-icon><Document /></el-icon>
                  </div>
                </div>
                
                <!-- 入口类字段 -->
                <div v-else-if="field.value === 'entry_class'" class="copyable-field" @click="copyToClipboard(row.entry_class, '入口类')">
                  <el-text size="small">{{ row.entry_class || '-' }}</el-text>
                  <div class="copy-hint">
                    <el-icon><Document /></el-icon>
                  </div>
                </div>
                
                <!-- 程序参数字段 -->
                <div v-else-if="field.value === 'program_args'" class="copyable-field" @click="copyToClipboard(row.program_args, '程序参数')">
                  <el-text size="small">{{ row.program_args || '-' }}</el-text>
                  <div class="copy-hint">
                    <el-icon><Document /></el-icon>
                  </div>
                </div>
                
                <!-- 最新JAR路径字段 -->
                <div v-else-if="field.value === 'last_jar_path'" class="copyable-field" @click="copyToClipboard(row.last_jar_path, '最新JAR路径')">
                  <el-text size="small">{{ row.last_jar_path || '-' }}</el-text>
                  <div class="copy-hint">
                    <el-icon><Document /></el-icon>
                  </div>
                </div>
                
                <!-- 错误信息字段 -->
                <div v-else-if="field.value === 'error_message'" class="copyable-field" @click="copyToClipboard(row.error_message, '错误信息')">
                  <el-text size="small" type="danger">{{ row.error_message || '-' }}</el-text>
                  <div class="copy-hint">
                    <el-icon><Document /></el-icon>
                  </div>
                </div>
                
                <!-- 其他文本字段 -->
                <el-text v-else size="small">
                  {{ row[field.value] || '-' }}
                </el-text>
              </template>
          </el-table-column>
            </template>
          
          <el-table-column label="操作" width="220" fixed="right">
            <template #default="{ row }">
              <div class="action-buttons">
                <el-tooltip content="编辑作业配置" placement="top">
                  <el-button
                    type="primary"
                    size="small"
                    @click="handleEditJob(row)"
                    circle
                  >
                    <el-icon><Edit /></el-icon>
                  </el-button>
                </el-tooltip>
                <el-tooltip content="重启作业" placement="top">
                  <el-button
                    type="success"
                    size="small"
                    @click="handleRestartJob(row)"
                    circle
                  >
                    <el-icon><RefreshRight /></el-icon>
                  </el-button>
                </el-tooltip>
                <el-tooltip content="停止作业" placement="top">
                  <el-button
                    type="warning"
                    size="small"
                    @click="handleStopJob(row)"
                    circle
                  >
                    <el-icon><VideoPause /></el-icon>
                  </el-button>
                </el-tooltip>
                <el-tooltip content="删除作业" placement="top">
                  <el-button
                    type="danger"
                    size="small"
                    @click="handleDeleteJob(row)"
                    circle
                  >
                    <el-icon><Delete /></el-icon>
                  </el-button>
                </el-tooltip>
              </div>
            </template>
          </el-table-column>
        </el-table>
        </div>
        
        <!-- 可折叠分页 -->
        <div v-show="paginationExpanded" class="pagination">
          <el-pagination
            v-model:current-page="currentPage"
            v-model:page-size="pageSize"
            :page-sizes="[10, 20, 50, 100]"
            :total="total"
            layout="total, sizes, prev, pager, next, jumper"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            class="enhanced-pagination"
          />
        </div>
      </el-card>
    </div>


    <!-- 作业编辑对话框 -->
    <JobEditDialog
      v-model="showEditDialog"
      :job="currentJob"
      @success="handleEditSuccess"
    />

    <!-- 作业重启对话框 -->
    <JobRestartDialog
      v-model="showRestartDialog"
      :job="currentJob"
      @success="handleRestartSuccess"
    />

    <!-- 单个作业停止对话框 -->
    <JobStopDialog
      v-model="showJobStopDialog"
      :job="selectedJob"
      @success="handleJobStopSuccess"
    />

    <!-- 批量停止对话框 -->
    <BulkStopDialog
      v-model="showBulkStopDialog"
      :jobs="selectedJobs"
      @success="handleBulkStopSuccess"
      @start-batch="handleStartBulkStop"
    />

    <!-- 批量重启对话框 -->
    <BulkRestartDialog
      v-model="showBulkRestartDialog"
      :jobs="selectedJobs"
      @success="handleBulkRestartSuccess"
      @start-batch="handleStartBulkRestart"
    />

    <!-- 批量进度对话框 -->
    <BulkProgressDialog
      ref="bulkProgressRef"
      v-model="showBulkProgressDialog"
      title="批量操作进度"
      subtitle="正在处理批量作业操作"
      @background="handleBackgroundTask"
    />

    <!-- 后台任务浮动按钮 - 右侧边栏样式 -->
    <transition name="slide-fade">
      <div
        v-if="hasBackgroundTask"
        class="background-task-indicator"
        :class="{ collapsed: !isSidebarExpanded }"
        :title="isSidebarExpanded ? '单击收起，双击查看详情' : '单击展开'"
      >
        <!-- 收起状态：只显示箭头 -->
        <div 
          v-if="!isSidebarExpanded" 
          class="arrow-icon"
          @click="isSidebarExpanded = true"
        >
          <el-icon><DArrowLeft /></el-icon>
        </div>
        
        <!-- 展开状态：显示完整内容 -->
        <div 
          v-else
          class="task-content-wrapper"
          @click="handleSingleClick"
          @dblclick="handleDoubleClick"
        >
          <div class="task-icon">
            <el-icon v-if="backgroundTaskCompleted" class="icon-success"><CircleCheck /></el-icon>
            <el-icon v-else class="icon-loading"><Loading /></el-icon>
          </div>
          <div class="task-text">
            <div class="task-label">{{ backgroundTaskCompleted ? '已完成' : '进行中' }}</div>
            <div class="task-progress">{{ backgroundTaskProgress }}</div>
          </div>
        </div>
      </div>
    </transition>

    <!-- 批量删除对话框 -->
    <el-dialog
      v-model="showBulkDeleteDialog"
      width="500px"
      :close-on-click-modal="false"
      class="unified-dialog"
    >
      <template #header>
        <div class="dialog-header">
          <div class="header-icon header-icon-danger">
            <el-icon><Warning /></el-icon>
          </div>
          <div class="header-content">
            <h3>批量删除确认</h3>
            <p>此操作将清空整个作业元数据表，请谨慎操作</p>
          </div>
        </div>
      </template>
      
      <div class="dialog-content">
        <el-alert
          title="⚠️ 危险操作"
          type="error"
          :closable="false"
          show-icon
        >
          <template #default>
            <p style="margin: 8px 0;">此操作将<strong>清空整个flink_cluster_job_metadata表</strong>，删除所有作业记录！</p>
            <p style="margin: 8px 0 0 0;">此操作<strong>不可撤销</strong>，请谨慎操作！</p>
          </template>
        </el-alert>
      </div>
      
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="showBulkDeleteDialog = false">取消</el-button>
          <el-button type="danger" @click="confirmBulkDelete" :loading="isDeleting">
            <el-icon><Delete /></el-icon>
            确认清空表
          </el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 发现未注册作业对话框 -->
    <JobDiscoveryDialog
      v-model="showDiscoveryDialog"
      @success="handleDiscoverySuccess"
    />

    <!-- 作业注册对话框 -->
    <el-dialog
      v-model="showRegisterDialog"
      width="700px"
      :close-on-click-modal="false"
      class="unified-dialog"
    >
      <template #header>
        <div class="dialog-header">
          <div class="header-icon header-icon-primary">
            <el-icon><Plus /></el-icon>
          </div>
          <div class="header-content">
            <h3>注册新作业</h3>
            <p>创建新的Flink作业配置</p>
          </div>
        </div>
      </template>
      <el-form :model="registerForm" label-width="120px" :rules="registerRules" ref="registerFormRef" class="job-register-form">
        <!-- 基础信息分组 -->
        <div class="form-section">
          <div class="section-title">
            <el-icon><Document /></el-icon>
            <span>基础信息</span>
          </div>
          
        <el-form-item label="作业名称" prop="job_name">
          <el-input v-model="registerForm.job_name" placeholder="请输入作业名称" />
        </el-form-item>
        
        <el-form-item label="作业描述" prop="job_description">
          <el-input 
            v-model="registerForm.job_description" 
            type="textarea" 
            :rows="2"
            placeholder="请输入作业描述（可选）" 
          />
        </el-form-item>
        
        <el-form-item label="JAR包" prop="jar_name">
          <el-input v-model="registerForm.jar_name" placeholder="请输入JAR文件名，例如 demo.jar" />
        </el-form-item>
        <el-form-item label="JAR基路径" prop="jar_base_path">
          <el-input v-model="registerForm.jar_base_path" placeholder="/opt/flink/userjars/flink-web-upload" />
          <span class="form-tip">可修改为你的上传目录，未填写默认使用 /opt/flink/userjars/flink-web-upload</span>
        </el-form-item>
        
        <el-form-item label="入口类" prop="entry_class">
          <el-input v-model="registerForm.entry_class" placeholder="请输入主类名，如：com.example.MainClass" />
        </el-form-item>
        
        <el-form-item label="程序参数" prop="program_args">
          <el-input 
            v-model="registerForm.program_args" 
            type="textarea" 
            :rows="2"
            placeholder="请输入程序参数（可选）" 
          />
        </el-form-item>
        
        <el-form-item label="作业ID" prop="job_id">
          <el-input v-model="registerForm.job_id" placeholder="请输入Flink作业ID（可选）" />
        </el-form-item>
        </div>
        
        <!-- 恢复设置分组 -->
        <div class="form-section">
          <div class="section-title">
            <el-icon><Refresh /></el-icon>
            <span>恢复设置</span>
          </div>
        
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="自动恢复" prop="auto_recovery">
                <div class="switch-container">
              <el-switch v-model="registerForm.auto_recovery" />
                  <div class="switch-description">
                    <p class="description-title">启用自动恢复</p>
                    <p class="description-text">作业异常停止时自动重启</p>
                  </div>
                </div>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="最大恢复次数" prop="max_recovery_attempts">
                <el-input-number v-model="registerForm.max_recovery_attempts" :min="1" :max="10" class="number-input" />
                <span class="form-tip">建议设置为3-5次</span>
            </el-form-item>
          </el-col>
        </el-row>
        </div>
        
        <!-- 保存点设置分组 -->
        <div class="form-section">
          <div class="section-title">
            <el-icon><Clock /></el-icon>
            <span>保存点设置</span>
          </div>
        
        <el-form-item label="保存点间隔" prop="savepoint_interval">
            <el-input-number v-model="registerForm.savepoint_interval" :min="1" :max="168" class="number-input" />
            <span class="form-tip">小时，建议设置为24小时</span>
        </el-form-item>
        </div>
      </el-form>
      
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="showRegisterDialog = false">取消</el-button>
          <el-button type="primary" @click="handleRegisterJob" :loading="isRegistering">
            <el-icon><Check /></el-icon>
            注册作业
          </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>


<script setup>
import { ref, onMounted, computed, nextTick } from 'vue'
import { storeToRefs } from 'pinia'
import { useJobsStore } from '@/stores/jobs'
import { useAppStore } from '@/stores/app'
import { ElMessage, ElMessageBox } from 'element-plus'
import { componentLogger } from '@/utils/logger'
import { copyToClipboard as copyText } from '@/utils/clipboard'
import dayjs from 'dayjs'
import { debounce } from 'lodash-es'
import { VueDraggableNext } from 'vue-draggable-next'
import FieldSelector from '@/components/FieldSelector.vue'
import JobEditDialog from './components/JobEditDialog.vue'
import JobRestartDialog from './components/JobRestartDialog.vue'
import JobStopDialog from './components/JobStopDialog.vue'
import BulkStopDialog from './components/BulkStopDialog.vue'
import BulkRestartDialog from './components/BulkRestartDialog.vue'
import BulkProgressDialog from './components/BulkProgressDialog.vue'
import JobDiscoveryDialog from './components/JobDiscoveryDialog.vue'

const jobsStore = useJobsStore()
const appStore = useAppStore()
const { sidebarCollapsed } = storeToRefs(appStore)

// API 基础 URL
const apiBaseUrl = '/api'

// 字段设置保存和加载函数
const loadFieldSettings = (key, defaultValue) => {
  try {
    // 优先加载用户自定义的默认设置
    const defaultKey = `jobs_default${key.charAt(0).toUpperCase() + key.slice(1)}`
    const defaultSaved = localStorage.getItem(defaultKey)
    if (defaultSaved) {
      return JSON.parse(defaultSaved)
    }
    
    // 如果没有自定义默认设置，加载当前设置
    const saved = localStorage.getItem(`jobs_${key}`)
    return saved ? JSON.parse(saved) : defaultValue
  } catch (error) {
    componentLogger.error('加载字段设置失败:', error)
    return defaultValue
  }
}

const saveFieldSettings = (key, value) => {
  try {
    localStorage.setItem(`jobs_${key}`, JSON.stringify(value))
  } catch (error) {
    componentLogger.error('保存字段设置失败:', error)
  }
}

// 保存当前字段设置
const saveCurrentFieldSettings = () => {
  saveFieldSettings('selectedFields', selectedFields.value)
  saveFieldSettings('fieldOrder', fieldOrder.value)
  ElMessage.success('字段设置已保存')
  showFieldSelector.value = false
}

// 将当前设置设为默认值
const setAsDefaultSettings = () => {
  ElMessageBox.confirm(
    '确定要将当前字段选择和排序设为默认设置吗？',
    '确认设为默认',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'info'
    }
  ).then(() => {
    // 保存当前设置为默认值
    saveFieldSettings('defaultSelectedFields', selectedFields.value)
    saveFieldSettings('defaultFieldOrder', fieldOrder.value)
    
    ElMessage.success('当前设置已设为默认值')
  }).catch(() => {
    // 用户取消操作
  })
}

// 清除自定义默认设置
const clearDefaultSettings = () => {
  ElMessageBox.confirm(
    '确定要清除自定义的默认设置吗？清除后将使用系统默认设置。',
    '确认清除默认设置',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }
  ).then(() => {
    // 清除自定义默认设置
    localStorage.removeItem('jobs_defaultSelectedFields')
    localStorage.removeItem('jobs_defaultFieldOrder')
    
    ElMessage.success('自定义默认设置已清除')
  }).catch(() => {
    // 用户取消操作
  })
}

// 重置字段设置为默认值
const resetFieldSettings = () => {
  ElMessageBox.confirm(
    '确定要重置字段设置为默认值吗？',
    '确认重置',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }
  ).then(() => {
    // 尝试加载用户自定义的默认设置
    const defaultSelectedFields = loadFieldSettings('defaultSelectedFields', null)
    const defaultFieldOrder = loadFieldSettings('defaultFieldOrder', null)
    
    if (defaultSelectedFields && defaultFieldOrder) {
      // 使用用户自定义的默认设置
      selectedFields.value = defaultSelectedFields
      fieldOrder.value = defaultFieldOrder
    } else {
      // 使用系统默认设置
      selectedFields.value = [
        'job_name', 'job_id', 'jar_name', 'entry_class', 'last_status', 
        'auto_recovery', 'manual_stopped', 'is_alert_enabled', 'monitor_status', 'recovery_attempts', 'last_update_time',
        'job_description', 'program_args', 'max_recovery_attempts', 'savepoint_interval'
      ]
      fieldOrder.value = [
        'job_name', 'job_id', 'job_description', 'jar_name', 'jar_id', 
        'init_jar_path', 'last_jar_path', 'entry_class', 'program_args',
        'last_status', 'auto_recovery', 'recovery_attempts', 'max_recovery_attempts',
        'monitor_status', 'consecutive_failures', 'manual_stopped', 'is_alert_enabled', 'health_status',
        'registration_time', 'last_update_time', 'last_check_time', 'last_running_time',
        'last_stop_time', 'last_successful_check', 'last_health_check',
        'savepoint_interval', 'last_savepoint_path', 'last_savepoint_time',
        'error_message', 'last_error_type', 'last_error_time', 'version'
      ]
    }
    
    // 保存重置后的设置
    saveFieldSettings('selectedFields', selectedFields.value)
    saveFieldSettings('fieldOrder', fieldOrder.value)
    
    // 重新获取数据
    fetchJobs()
    
    ElMessage.success('字段设置已重置为默认值')
  }).catch(() => {
    // 用户取消重置
  })
}

// 响应式数据
const selectedJobs = ref([])
const selectedJob = ref(null)
const showEditDialog = ref(false)
const showRestartDialog = ref(false)
const showJobStopDialog = ref(false)
const showBulkStopDialog = ref(false)
const showBulkRestartDialog = ref(false)
const showBulkProgressDialog = ref(false)
const bulkProgressRef = ref(null)
const showDiscoveryDialog = ref(false)
const showBulkDeleteDialog = ref(false)
const isDeleting = ref(false)
const paginationExpanded = ref(false)

// 后台任务状态
const hasBackgroundTask = ref(false)
const backgroundTaskProgress = ref(0)
const backgroundTaskCompleted = ref(false)
const currentTaskId = ref(null)  // 当前任务ID

// 侧边栏展开/收起状态
const isSidebarExpanded = ref(true)
const clickTimer = ref(null)
const showRegisterDialog = ref(false)
const isRegistering = ref(false)
const showFieldSelector = ref(false)

// 作业注册表单
const registerForm = ref({
  job_name: '',
  job_description: '',
  jar_name: '',
  jar_base_path: '/opt/flink/userjars/flink-web-upload',
  entry_class: '',
  program_args: '',
  auto_recovery: true,
  max_recovery_attempts: 3,
  savepoint_interval: 24,
  job_id: ''
})
const currentJob = ref(null)
const isInitializing = ref(true)
const isRefreshing = ref(false)

// 注册表单验证规则
const registerRules = {
  job_name: [
    { required: true, message: '请输入作业名称', trigger: 'blur' },
    { min: 2, max: 50, message: '作业名称长度在 2 到 50 个字符', trigger: 'blur' }
  ],
  jar_name: [
    { required: true, message: '请输入JAR文件名（例如 demo.jar）', trigger: 'blur' }
  ],
  entry_class: [
    { required: true, message: '请输入入口类名', trigger: 'blur' },
    { pattern: /^[a-zA-Z][a-zA-Z0-9_.]*$/, message: '请输入有效的Java类名', trigger: 'blur' }
  ]
}

// 字段选择 - 从localStorage加载或使用默认值
const selectedFields = ref(loadFieldSettings('selectedFields', [
  'job_name', 'job_id', 'jar_name', 'entry_class', 'last_status', 
  'auto_recovery', 'manual_stopped', 'is_alert_enabled', 'monitor_status', 'recovery_attempts', 'last_update_time',
  'job_description', 'program_args', 'max_recovery_attempts', 'savepoint_interval'
]))

// 字段排序 - 从localStorage加载或使用默认值
const fieldOrder = ref(loadFieldSettings('fieldOrder', [
  'job_name', 'job_id', 'job_description', 'jar_name', 'jar_id', 
  'init_jar_path', 'last_jar_path', 'entry_class', 'program_args',
  'last_status', 'auto_recovery', 'recovery_attempts', 'max_recovery_attempts',
  'monitor_status', 'consecutive_failures', 'manual_stopped', 'is_alert_enabled', 'health_status',
  'registration_time', 'last_update_time', 'last_check_time', 'last_running_time',
  'last_stop_time', 'last_successful_check', 'last_health_check',
  'savepoint_interval', 'last_savepoint_path', 'last_savepoint_time',
  'error_message', 'last_error_type', 'last_error_time', 'version'
]))

// 字段配置
const fieldConfig = {
  basic: [
    { value: 'job_name', label: '作业名称', required: true, default: true },
    { value: 'job_id', label: '作业ID', default: true },
    { value: 'job_description', label: '作业描述' },
    { value: 'jar_name', label: 'JAR包', default: true },
    { value: 'jar_id', label: 'JAR ID' },
    { value: 'init_jar_path', label: '初始化JAR路径' },
    { value: 'last_jar_path', label: '最新JAR路径' },
    { value: 'entry_class', label: '入口类', default: true },
    { value: 'program_args', label: '程序参数' }
  ],
  status: [
    { value: 'last_status', label: '状态', default: true },
    { value: 'auto_recovery', label: '自动恢复', default: true },
    { value: 'recovery_attempts', label: '恢复次数', default: true },
    { value: 'max_recovery_attempts', label: '最大恢复次数' },
    { value: 'monitor_status', label: '监控状态', default: true },
    { value: 'total_failures', label: '总失败次数' },
    { value: 'consecutive_failures', label: '连续失败次数' },
    { value: 'manual_stopped', label: '是否监控' },
    { value: 'is_alert_enabled', label: '告警开关' },
    { value: 'health_status', label: '健康状态' }
  ],
  time: [
    { value: 'registration_time', label: '注册时间' },
    { value: 'last_update_time', label: '更新时间', default: true },
    { value: 'last_check_time', label: '最后检查时间' },
    { value: 'last_running_time', label: '最后运行时间' },
    { value: 'last_stop_time', label: '最后停止时间' },
    { value: 'last_successful_check', label: '最后成功检查时间' },
    { value: 'last_health_check', label: '最后健康检查时间' }
  ],
  savepoint: [
    { value: 'savepoint_interval', label: '保存点间隔(小时)' },
    { value: 'last_savepoint_path', label: '最后保存点路径' },
    { value: 'last_savepoint_time', label: '最后保存点时间' }
  ],
  error: [
    { value: 'error_message', label: '错误信息' },
    { value: 'last_error_type', label: '最后错误类型' },
    { value: 'last_error_time', label: '最后错误时间' }
  ],
  system: [
    { value: 'version', label: '版本号' }
  ]
}

// 计算属性 - 使用缓存优化性能
const jobs = computed(() => jobsStore.jobs)
const loading = computed(() => jobsStore.loading)
const total = computed(() => jobsStore.total)
const totalAll = computed(() => jobsStore.totalAll)
const statusStats = computed(() => jobsStore.statusStats)
const currentPage = computed({
  get: () => jobsStore.currentPage,
  set: (value) => { jobsStore.currentPage = value }
})
const pageSize = computed({
  get: () => jobsStore.pageSize,
  set: (value) => { jobsStore.pageSize = value }
})
const searchForm = computed(() => jobsStore.searchForm)

// 生成所有字段的扁平列表
const allFields = computed(() => {
  const fields = []
  Object.entries(fieldConfig).forEach(([group, groupFields]) => {
    groupFields.forEach(field => {
      fields.push({
        ...field,
        group
      })
    })
  })
  return fields
})

// 按排序顺序生成字段列表
const sortedFields = computed(() => {
  const fieldMap = new Map(allFields.value.map(field => [field.value, field]))
  const orderedFields = fieldOrder.value
    .map(fieldValue => fieldMap.get(fieldValue))
    .filter(Boolean)
  
  // 添加不在 fieldOrder 中的新字段（按 fieldConfig 的顺序）
  const orderedFieldValues = new Set(fieldOrder.value)
  const unorderedFields = allFields.value
    .filter(field => !orderedFieldValues.has(field.value))
  
  return [...orderedFields, ...unorderedFields]
})

// 计算当前所有作业的监控状态
const allMonitorActive = computed(() => {
  const currentJobs = jobs.value
  if (!currentJobs || currentJobs.length === 0) return false
  // 如果大部分作业都在监控中，则显示"停止监控"
  const activeCount = currentJobs.filter(job => 
    job.manual_stopped === 0 && job.monitor_status === 'ACTIVE'
  ).length
  return activeCount > currentJobs.length / 2
})

// 计算当前所有作业的自动恢复状态
const allRecoveryActive = computed(() => {
  const currentJobs = jobs.value
  if (!currentJobs || currentJobs.length === 0) return false
  // 如果大部分作业都开启了自动恢复，则显示"停止自动恢复"
  const activeCount = currentJobs.filter(job => job.auto_recovery === 1).length
  return activeCount > currentJobs.length / 2
})

// 计算当前所有选中作业的告警状态
const allAlertEnabled = computed(() => {
  const selected = selectedJobs.value
  if (!selected || selected.length === 0) return false
  // 如果大部分选中的作业都开启了告警，则显示"关闭告警"
  const enabledCount = selected.filter(job => job.is_alert_enabled === 1).length
  return enabledCount > selected.length / 2
})

// 固定容器样式（不响应侧边栏折叠）
const containerStyle = {
  padding: '0'
}

// 缓存状态类型计算
const statusTypeCache = new Map()
const getStatusType = (status) => {
  if (statusTypeCache.has(status)) {
    return statusTypeCache.get(status)
  }
  const statusMap = {
    'RUNNING': 'success',
    'FINISHED': 'info',
    'FAILED': 'danger',
    'CANCELED': 'warning',
    'REGISTERED': 'primary'
  }
  const result = statusMap[status] || 'info'
  statusTypeCache.set(status, result)
  return result
}

const monitorStatusTypeCache = new Map()

const healthStatusTypeCache = new Map()
const getHealthStatusType = (status) => {
  if (healthStatusTypeCache.has(status)) {
    return healthStatusTypeCache.get(status)
  }
  const statusMap = {
    'HEALTHY': 'success',
    'UNHEALTHY': 'danger',
    'WARNING': 'warning',
    'UNKNOWN': 'info'
  }
  const result = statusMap[status] || 'info'
  healthStatusTypeCache.set(status, result)
  return result
}

// 方法
const handleSearch = () => {
  currentPage.value = 1
  fetchJobs()
}

const handleReset = () => {
  jobsStore.resetSearch()
  fetchJobs()
}

const handleRefresh = () => {
  fetchJobs()
}

const handleSelectionChange = (selection) => {
  selectedJobs.value = selection
}

// 处理排序变化
const handleSortChange = ({ prop, order }) => {
  componentLogger.info('Sort change:', { prop, order })
  if (!prop || !order) {
    // 取消排序，恢复默认（按更新时间降序）
    jobsStore.sortField = 'last_update_time'
    jobsStore.sortOrder = 'desc'
  } else {
    // 设置排序
    jobsStore.sortField = prop
    jobsStore.sortOrder = order === 'ascending' ? 'asc' : 'desc'
  }
  // 刷新数据
  fetchJobs()
}

const handleSizeChange = (size) => {
  pageSize.value = size
  fetchJobs()
}

const handleCurrentChange = (page) => {
  currentPage.value = page
  fetchJobs()
}

const fetchJobs = async () => {
  try {
    isRefreshing.value = true
    
    // 如果还没有全部数据统计，先获取
    if (!jobsStore.totalAll) {
      await jobsStore.fetchAllStats()
    }
    
    await jobsStore.fetchJobs({
      fields: selectedFields.value,
      field_order: fieldOrder.value
    })
    // 数据加载完成后，设置初始化完成标志
    isInitializing.value = false
  } catch (error) {
    componentLogger.error('获取作业列表失败:', error)
    isInitializing.value = false
  } finally {
    // 延迟重置刷新标志，避免开关控件立即触发事件
    setTimeout(() => {
      isRefreshing.value = false
    }, 500)
  }
}


// 缓存时间格式化结果
const timeFormatCache = new Map()
const formatTime = (time) => {
  if (!time) return '-'
  if (timeFormatCache.has(time)) {
    return timeFormatCache.get(time)
  }
  const formatted = dayjs(time).format('YYYY-MM-DD HH:mm:ss')
  timeFormatCache.set(time, formatted)
  return formatted
}



const handleToggleAutoRecovery = async (row, newValue) => {
  try {
    // 防止初始化或刷新时触发
    if (isInitializing.value || isRefreshing.value) {
      componentLogger.info('Still initializing or refreshing, skipping auto recovery toggle...')
      return
    }
    
    if (!row || typeof row.auto_recovery === 'undefined') {
      componentLogger.info('Row object invalid or auto_recovery undefined, skipping...')
      return
    }
    
    // 等待数据加载完成
    if (jobs.value.length === 0) {
      componentLogger.info('Jobs array is empty, waiting for data to load...')
      await new Promise(resolve => setTimeout(resolve, 100))
    }
    
    // 如果数据仍然为空，重新获取
    if (jobs.value.length === 0) {
      componentLogger.info('Refreshing jobs data...')
      await jobsStore.fetchJobs()
    }
    
    // 通过job_name查找对应的作业（因为row.auto_recovery已经被v-model更新了）
    const matchingJob = jobs.value.find(j => j.job_name === row.job_name)
    
    if (matchingJob && matchingJob.job_name) {
      const action = newValue === 1 ? '开启' : '关闭'
      await ElMessageBox.confirm(
        `确定要${action}作业 "${matchingJob.job_name}" 的自动恢复吗？`,
        `确认${action}自动恢复`,
        { confirmButtonText: '确定', cancelButtonText: '取消', type: newValue === 1 ? 'info' : 'warning' }
      )
      
      const response = await jobsStore.toggleAutoRecovery(matchingJob.job_name, newValue)
      if (response.success) {
        ElMessage.success(`自动恢复已${action}`)
        // 更新本地状态
        row.auto_recovery = newValue
        // 更新监控状态
        updateMonitorStatus(row)
        // 刷新数据以显示最新状态
        await fetchJobs()
        return
      } else {
        ElMessage.error(response.message || `${action}失败`)
        row.auto_recovery = newValue ? 0 : 1
        return
      }
    }
    
    // 如果还是找不到，显示错误
    componentLogger.error('Job not found in jobs array:', row.job_name)
    ElMessage.error('找不到对应的作业')
    row.auto_recovery = newValue ? 0 : 1
  } catch (error) {
    if (error !== 'cancel') {
      row.auto_recovery = newValue ? 0 : 1
      componentLogger.error('自动恢复切换失败:', error)
      ElMessage.error('自动恢复切换失败: ' + (error.message || '未知错误'))
    } else {
      row.auto_recovery = newValue ? 0 : 1
    }
  }
}

// 是否监控切换功能
const handleToggleManualStopped = async (row, newValue) => {
  try {
    // 防止初始化或刷新时触发
    if (isInitializing.value || isRefreshing.value) {
      componentLogger.info('Still initializing or refreshing, skipping monitor toggle...')
      return
    }
    
    if (!row || typeof row.manual_stopped === 'undefined') {
      componentLogger.info('Row object invalid or manual_stopped undefined, skipping...')
      return
    }
    
    const action = newValue === 0 ? '开始' : '停止'
    await ElMessageBox.confirm(
      `确定要${action}监控作业 "${row.job_name}" 吗？`,
      `确认${action}监控`,
      { confirmButtonText: '确定', cancelButtonText: '取消', type: newValue === 0 ? 'info' : 'warning' }
    )
    
    const response = await jobsStore.toggleManualStopped(row.job_name, newValue)
    if (response.success) {
      ElMessage.success(`作业监控已${action}`)
      row.manual_stopped = newValue
      // 如果停止监控（manual_stopped=1），自动关闭告警（is_alert_enabled=0）
      if (newValue === 1) {
        row.is_alert_enabled = 0
      }
      // 更新监控状态
      updateMonitorStatus(row)
    } else {
      row.manual_stopped = newValue === 0 ? 1 : 0
      ElMessage.error(response.message || `${action}失败`)
    }
  } catch (error) {
    if (error !== 'cancel') {
      row.manual_stopped = newValue === 0 ? 1 : 0
      componentLogger.error('监控状态切换失败:', error)
      ElMessage.error('监控状态切换失败: ' + (error.message || '未知错误'))
    } else {
      row.manual_stopped = newValue === 0 ? 1 : 0
    }
  }
}

// 告警开关切换功能
const handleToggleAlertEnabled = async (row, newValue) => {
  try {
    // 防止初始化或刷新时触发
    if (isInitializing.value || isRefreshing.value) {
      componentLogger.info('Still initializing or refreshing, skipping alert enabled toggle...')
      return
    }
    
    if (!row || !row.job_name) {
      componentLogger.info('Row object invalid or job_name missing, skipping...')
      return
    }
    
    // 检查字段选择器是否打开，如果打开则不处理（避免字段选择器操作时误触发）
    if (showFieldSelector.value) {
      componentLogger.info('Field selector is open, skipping alert enabled toggle...')
      return
    }
    
    // 如果 manual_stopped=1（不监控），不允许开启告警
    if (row.manual_stopped === 1 && newValue === 1) {
      ElMessage.warning('作业已停止监控，无法开启告警。请先开启监控')
      row.is_alert_enabled = 0
      return
    }
    
    // 等待数据加载完成
    if (jobs.value.length === 0) {
      componentLogger.info('Jobs array is empty, waiting for data to load...')
      await new Promise(resolve => setTimeout(resolve, 100))
    }
    
    // 如果数据仍然为空，重新获取
    if (jobs.value.length === 0) {
      componentLogger.info('Refreshing jobs data...')
      await jobsStore.fetchJobs()
    }
    
    // 通过job_name查找对应的作业（因为row.is_alert_enabled已经被v-model更新了）
    const matchingJob = jobs.value.find(j => j.job_name === row.job_name)
    
    if (matchingJob && matchingJob.job_name) {
      const action = newValue === 1 ? '开启' : '关闭'
      await ElMessageBox.confirm(
        `确定要${action}作业 "${matchingJob.job_name}" 的告警吗？`,
        `确认${action}告警`,
        { confirmButtonText: '确定', cancelButtonText: '取消', type: newValue === 1 ? 'info' : 'warning' }
      )
      
      const response = await jobsStore.toggleAlertEnabled(matchingJob.job_name, newValue)
      if (response.success) {
        ElMessage.success(`告警已${action}`)
        // 更新本地状态
        row.is_alert_enabled = newValue
        // 刷新数据以显示最新状态
        await fetchJobs()
        return
      } else {
        ElMessage.error(response.message || `${action}失败`)
        row.is_alert_enabled = newValue === 1 ? 0 : 1
        return
      }
    }
    
    // 如果还是找不到，显示错误
    componentLogger.error('Job not found in jobs array:', row.job_name)
    ElMessage.error('找不到对应的作业')
    row.is_alert_enabled = newValue === 1 ? 0 : 1
  } catch (error) {
    if (error !== 'cancel') {
      row.is_alert_enabled = newValue === 1 ? 0 : 1
      componentLogger.error('告警开关切换失败:', error)
      ElMessage.error('告警开关切换失败: ' + (error.message || '未知错误'))
    } else {
      row.is_alert_enabled = newValue === 1 ? 0 : 1
    }
  }
}

// 增加最大恢复次数
const increaseMaxRecoveryAttempts = async (row) => {
  try {
    if (isInitializing.value || isRefreshing.value) {
      componentLogger.info('Still initializing or refreshing, skipping max recovery attempts increase...')
      return
    }
    
    if (!row || !row.job_name) {
      componentLogger.info('Row object invalid or job_name missing, skipping...')
      return
    }
    
    const newValue = Math.min((row.max_recovery_attempts || 0) + 1, 100)
    
    const response = await jobsStore.updateJobAttempts(row.job_name, {
      max_recovery_attempts: newValue,
      recovery_attempts: row.recovery_attempts || 0
    })
    
    if (response && response.success) {
      row.max_recovery_attempts = newValue
      ElMessage.success(`最大恢复次数已更新为 ${newValue}`)
    } else {
      ElMessage.error(response?.message || '更新失败')
    }
  } catch (error) {
    componentLogger.error('增加最大恢复次数失败:', error)
    ElMessage.error('增加最大恢复次数失败: ' + (error.message || '未知错误'))
  }
}

// 减少最大恢复次数
const decreaseMaxRecoveryAttempts = async (row) => {
  try {
    if (isInitializing.value || isRefreshing.value) {
      componentLogger.info('Still initializing or refreshing, skipping max recovery attempts decrease...')
      return
    }
    
    if (!row || !row.job_name) {
      componentLogger.info('Row object invalid or job_name missing, skipping...')
      return
    }
    
    const newValue = Math.max((row.max_recovery_attempts || 0) - 1, 0)
    
    const response = await jobsStore.updateJobAttempts(row.job_name, {
      max_recovery_attempts: newValue,
      recovery_attempts: row.recovery_attempts || 0
    })
    
    if (response && response.success) {
      row.max_recovery_attempts = newValue
      ElMessage.success(`最大恢复次数已更新为 ${newValue}`)
    } else {
      ElMessage.error(response?.message || '更新失败')
    }
  } catch (error) {
    componentLogger.error('减少最大恢复次数失败:', error)
    ElMessage.error('减少最大恢复次数失败: ' + (error.message || '未知错误'))
  }
}

// 更新监控状态
const updateMonitorStatus = (row) => {
  const { manual_stopped, auto_recovery } = row
  
  if (manual_stopped === 1 && !auto_recovery) {
    row.monitor_status = 'DISABLED'
  } else if (manual_stopped === 1) {
    row.monitor_status = 'DISMONITOR'
  } else if (!auto_recovery) {
    row.monitor_status = 'DISRECOVER'
  } else {
    row.monitor_status = 'ACTIVE'
  }
}

// 获取监控状态类型
const getMonitorStatusType = (status) => {
  switch (status) {
    case 'ACTIVE': return 'success'
    case 'DISMONITOR': return 'warning'
    case 'DISRECOVER': return 'info'
    case 'DISABLED': return 'danger'
    default: return 'info'
  }
}

// 获取监控状态文本
const getMonitorStatusText = (status) => {
  switch (status) {
    case 'ACTIVE': return '监控中'
    case 'DISMONITOR': return '已停止监控'
    case 'DISRECOVER': return '禁用恢复'
    case 'DISABLED': return '完全禁用'
    default: return '未知'
  }
}

const handleEditJob = (job) => {
  currentJob.value = job
  showEditDialog.value = true
}

const handleRestartJob = (job) => {
  currentJob.value = job
  showRestartDialog.value = true
}

const handleStopJob = async (job) => {
  selectedJob.value = job
  showJobStopDialog.value = true
}

const handleDeleteJob = async (job) => {
  try {
    await ElMessageBox.confirm(
      `确定要删除作业 "${job.job_name}" 吗？此操作不可恢复！`,
      '确认删除',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'error'
      }
    )
    
    await jobsStore.deleteJob(job.job_name)
    // 删除成功后刷新列表，保持当前字段配置
    fetchJobs()
  } catch (error) {
    if (error !== 'cancel') {
      componentLogger.error('删除作业失败:', error)
    }
  }
}


const handleEditSuccess = () => {
  showEditDialog.value = false
  fetchJobs()
}

const handleRestartSuccess = () => {
  showRestartDialog.value = false
  fetchJobs()
}

const handleJobStopSuccess = () => {
  showJobStopDialog.value = false
  selectedJob.value = null
  fetchJobs()
}

const handleBulkStopSuccess = () => {
  showBulkStopDialog.value = false
  selectedJobs.value = []
  fetchJobs()
}

const handleBulkRestartSuccess = () => {
  showBulkRestartDialog.value = false
  selectedJobs.value = []
  fetchJobs()
}

// 处理后台任务
const handleBackgroundTask = (progress) => {
  hasBackgroundTask.value = true
  backgroundTaskProgress.value = `${progress.current}/${progress.total}`
  backgroundTaskCompleted.value = false
}

// 处理单击事件（延迟执行，给双击留时间）
const handleSingleClick = () => {
  // 清除之前的定时器
  if (clickTimer.value) {
    clearTimeout(clickTimer.value)
  }
  
  // 延迟300ms执行收起操作
  clickTimer.value = setTimeout(() => {
    isSidebarExpanded.value = false
    clickTimer.value = null
  }, 300)
}

// 处理双击事件
const handleDoubleClick = () => {
  // 取消单击的延迟执行
  if (clickTimer.value) {
    clearTimeout(clickTimer.value)
    clickTimer.value = null
  }
  
  // 立即打开进度对话框
  reopenProgressDialog()
}

// 重新打开进度对话框
const reopenProgressDialog = () => {
  console.log('[Jobs] 重新打开进度对话框')
  
  // 如果任务已完成且没有对话框引用，清除后台任务
  if (backgroundTaskCompleted.value && !bulkProgressRef.value) {
    hasBackgroundTask.value = false
    return
  }
  
  // 打开进度对话框
  showBulkProgressDialog.value = true
  
  console.log('[Jobs] 对话框已打开')
}

// 水平拖动相关函数
const startDragX = (e) => {
  // 记录开始时间和位置
  dragStartTime.value = Date.now()
  dragStartX.value = e.clientX
  hasMoved.value = false
  
  // 延迟判断是否真的要拖动（避免与双击冲突）
  setTimeout(() => {
    const timeSinceStart = Date.now() - dragStartTime.value
    // 如果超过150ms还按着，认为是拖动
    if (timeSinceStart >= 150 && !hasMoved.value) {
      isDraggingX.value = true
      document.addEventListener('mousemove', onDragX)
      document.addEventListener('mouseup', stopDragX)
    }
  }, 150)
  
  // 立即添加 mouseup 监听，以便取消拖动
  document.addEventListener('mouseup', stopDragX)
}

const onDragX = (e) => {
  if (!isDraggingX.value) {
    // 即使还没开始拖动，也检测鼠标移动
    const moved = Math.abs(e.clientX - dragStartX.value) > 5
    if (moved) {
      hasMoved.value = true
      isDraggingX.value = true
    } else {
      return
    }
  }
  
  const deltaX = dragStartX.value - e.clientX
  const newX = floatingButtonX.value + deltaX
  
  // 限制在合理范围内（距离右边 20px 到距离左边 20px）
  const maxX = window.innerWidth - 200 // 最多到左边
  const minX = 20 // 最少距离右边 20px
  
  floatingButtonX.value = Math.max(minX, Math.min(newX, maxX))
  dragStartX.value = e.clientX
  
  if (!document.querySelector('.background-task-indicator')) return
  document.querySelector('.background-task-indicator').style.cursor = 'grabbing'
}

const stopDragX = () => {
  isDraggingX.value = false
  hasMoved.value = false
  document.removeEventListener('mousemove', onDragX)
  document.removeEventListener('mouseup', stopDragX)
  
  if (document.querySelector('.background-task-indicator')) {
    document.querySelector('.background-task-indicator').style.cursor = 'grab'
  }
}

// 轮询任务状态
const pollTaskStatus = async (taskId) => {
  const pollInterval = 1000 // 每秒轮询一次
  
  while (true) {
    try {
      const response = await fetch(`${apiBaseUrl}/jobs/task/${taskId}`)
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`)
      }
      
      const data = await response.json()
      if (!data.success) {
        throw new Error(data.error || '获取任务状态失败')
      }
      
      const task = data.data
      
      // 更新进度对话框
      if (bulkProgressRef.value) {
        // 更新每个作业的状态
        task.results.forEach((result, index) => {
          if (result.success) {
            bulkProgressRef.value.markSuccess(index)
          } else {
            bulkProgressRef.value.markError(index, result.message || '操作失败')
          }
        })
      }
      
      // 检查任务是否完成
      if (task.status === 'completed' || task.status === 'failed') {
        if (task.status === 'failed') {
          ElMessage.error(`任务失败: ${task.error || '未知错误'}`)
        } else {
          ElMessage.success(`任务完成！成功: ${task.success_count}, 失败: ${task.failed_count}`)
        }
        
        // 标记进度对话框为完成
        if (bulkProgressRef.value) {
          bulkProgressRef.value.complete()
        }
        
        break
      }
      
      // 等待下一次轮询
      await new Promise(resolve => setTimeout(resolve, pollInterval))
      
    } catch (error) {
      componentLogger.error('轮询任务状态失败:', error)
      ElMessage.error(`轮询任务状态失败: ${error.message}`)
      break
    }
  }
}

// 处理批量重启操作（使用异步接口）
const handleStartBulkRestart = async ({ jobs, params }) => {
  showBulkProgressDialog.value = true
  hasBackgroundTask.value = false // 重置后台任务状态
  
  // 等待下一帧，确保对话框已渲染
  await nextTick()
  
  // 初始化进度对话框
  bulkProgressRef.value.initProgress(jobs)
  
  try {
    // 准备请求参数
    const jobNames = jobs.map(j => j.job_name)
    const requestParams = {
      job_names: jobNames,
      flink_env: params.flink_env,
      update_db: params.update_db || false  // 传递数据库更新标志，默认不更新
    }
    
    // 根据模式设置参数
    if (params.mode === 'unified') {
      requestParams.program_args = params.unified_args || ''
    }
    
    // 调用异步接口，立即返回任务ID
    const response = await fetch(`${apiBaseUrl}/jobs/bulk-restart-async`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(requestParams)
    })
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }
    
    const data = await response.json()
    
    if (!data.success) {
      throw new Error(data.error || '启动批量重启任务失败')
    }
    
    const taskId = data.task_id
    
    // 轮询任务状态
    await pollTaskStatus(taskId)
    
  } catch (error) {
    componentLogger.error('批量重启失败:', error)
    ElMessage.error(`批量重启失败: ${error.message}`)
    bulkProgressRef.value.complete()
  }
  
  // 标记后台任务完成
  if (hasBackgroundTask.value) {
    backgroundTaskCompleted.value = true
    // 3秒后自动隐藏后台任务按钮
    setTimeout(() => {
      hasBackgroundTask.value = false
    }, 3000)
  }
  
  // 清除选择并刷新列表
  selectedJobs.value = []
  fetchJobs()
}

// 处理批量停止操作（使用异步接口）
const handleStartBulkStop = async ({ jobs, params }) => {
  showBulkProgressDialog.value = true
  hasBackgroundTask.value = false // 重置后台任务状态
  
  // 等待下一帧，确保对话框已渲染
  await nextTick()
  
  // 初始化进度对话框
  bulkProgressRef.value.initProgress(jobs)
  
  try {
    // 准备请求参数
    const jobNames = jobs.map(j => j.job_name)
    const requestParams = {
      job_names: jobNames,
      flink_env: params.flink_env,
      savepoint: params.savepoint || false,
      update_db: params.update_db || false  // 传递数据库更新标志，默认不更新
    }
    
    // 调用异步接口，立即返回任务ID
    const response = await fetch(`${apiBaseUrl}/jobs/bulk-stop-async`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(requestParams)
    })
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }
    
    const data = await response.json()
    
    if (!data.success) {
      throw new Error(data.error || '启动批量停止任务失败')
    }
    
    const taskId = data.task_id
    
    // 轮询任务状态
    await pollTaskStatus(taskId)
    
  } catch (error) {
    componentLogger.error('批量停止失败:', error)
    ElMessage.error(`批量停止失败: ${error.message}`)
    bulkProgressRef.value.complete()
  }
  
  // 标记后台任务完成
  if (hasBackgroundTask.value) {
    backgroundTaskCompleted.value = true
    // 3秒后自动隐藏后台任务按钮
    setTimeout(() => {
      hasBackgroundTask.value = false
    }, 3000)
  }
  
  // 清除选择并刷新列表
  selectedJobs.value = []
  fetchJobs()
}

const handleBulkDeleteSuccess = () => {
  showBulkDeleteDialog.value = false
  selectedJobs.value = []
  fetchJobs()
}

const handleDiscoverySuccess = () => {
  showDiscoveryDialog.value = false
  fetchJobs()
  ElMessage.success('作业发现和注册完成')
}

const confirmBulkDelete = async () => {
  try {
    isDeleting.value = true
    const response = await jobsStore.bulkDelete()
    
    if (response.success) {
      ElMessage.success(`成功清空表，删除了 ${response.data.deleted_count} 条记录`)
      handleBulkDeleteSuccess()
    } else {
      ElMessage.error(response.message || '删除失败')
    }
  } catch (error) {
    componentLogger.error('批量删除失败:', error)
    ElMessage.error('删除失败: ' + (error.message || '未知错误'))
  } finally {
    isDeleting.value = false
  }
}

// 作业注册功能
const handleRegisterJob = async () => {
  try {
    isRegistering.value = true
    const response = await jobsStore.registerJob(registerForm.value)
    
    if (response && response.success) {
      ElMessage.success(response.message || '作业注册成功')
      showRegisterDialog.value = false
      resetRegisterForm()
      // 延迟刷新，确保注册完成后再刷新数据
      setTimeout(async () => {
        await fetchJobs()
      }, 200)
    } else {
      ElMessage.error(response?.message || '注册失败')
    }
  } catch (error) {
    componentLogger.error('作业注册失败:', error)
    ElMessage.error('注册失败: ' + (error.message || '未知错误'))
  } finally {
    isRegistering.value = false
  }
}

const resetRegisterForm = () => {
  registerForm.value = {
    job_name: 'T_Job_Kafka2SR_TDS',
    job_description: '',
    jar_name: 'yc-example-job-1.0-SNAPSHOT.jar',
    jar_base_path: '/opt/flink/userjars/flink-web-upload',
    entry_class: 'com.jcm.Example',
    program_args: '',
    auto_recovery: true,
    max_recovery_attempts: 3,
    savepoint_interval: 24,
    job_id: ''
  }
}

// 批量停止监控功能
const handleBulkStopMonitor = async () => {
  try {
    // 检查是否有选中的作业
    if (selectedJobs.value.length === 0) {
      ElMessage.warning('请先选择要操作的作业')
      return
    }
    
    const jobNames = selectedJobs.value.map(job => job.job_name)
    
    await ElMessageBox.confirm(
      `确定要停止 ${selectedJobs.value.length} 个作业的监控吗？`,
      '确认停止监控',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }
    )
    
    const response = await jobsStore.bulkStopMonitor(jobNames)
    if (response.success) {
      ElMessage.success(`已停止 ${response.data.affected_rows} 个作业的监控`)
      selectedJobs.value = []  // 清空选择
      fetchJobs()
    } else {
      ElMessage.error(response.message || '停止监控失败')
    }
  } catch (error) {
    if (error !== 'cancel') {
      componentLogger.error('批量停止监控失败:', error)
      ElMessage.error('停止监控失败: ' + (error.message || '未知错误'))
    }
  }
}

// 批量停止自动恢复功能
const handleBulkStopRecovery = async () => {
  try {
    // 检查是否有选中的作业
    if (selectedJobs.value.length === 0) {
      ElMessage.warning('请先选择要操作的作业')
      return
    }
    
    const jobNames = selectedJobs.value.map(job => job.job_name)
    
    await ElMessageBox.confirm(
      `确定要停止 ${selectedJobs.value.length} 个作业的自动恢复吗？`,
      '确认停止自动恢复',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }
    )
    
    const response = await jobsStore.bulkStopRecovery(jobNames)
    if (response.success) {
      ElMessage.success(`已停止 ${response.data.affected_rows} 个作业的自动恢复`)
      selectedJobs.value = []  // 清空选择
      fetchJobs()
    } else {
      ElMessage.error(response.message || '停止自动恢复失败')
    }
  } catch (error) {
    if (error !== 'cancel') {
      componentLogger.error('批量停止自动恢复失败:', error)
      ElMessage.error('停止自动恢复失败: ' + (error.message || '未知错误'))
    }
  }
}

// 批量开始监控功能
const handleBulkStartMonitor = async () => {
  try {
    // 检查是否有选中的作业
    if (selectedJobs.value.length === 0) {
      ElMessage.warning('请先选择要操作的作业')
      return
    }
    
    const jobNames = selectedJobs.value.map(job => job.job_name)
    
    await ElMessageBox.confirm(
      `确定要开始 ${selectedJobs.value.length} 个作业的监控吗？`,
      '确认开始监控',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'success'
      }
    )
    
    const response = await jobsStore.bulkStartMonitor(jobNames)
    if (response.success) {
      ElMessage.success(`已开始 ${response.data.affected_rows} 个作业的监控`)
      selectedJobs.value = []  // 清空选择
      fetchJobs()
    } else {
      ElMessage.error(response.message || '开始监控失败')
    }
  } catch (error) {
    if (error !== 'cancel') {
      componentLogger.error('批量开始监控失败:', error)
      ElMessage.error('开始监控失败: ' + (error.message || '未知错误'))
    }
  }
}

// 批量开始自动恢复功能
const handleBulkStartRecovery = async () => {
  try {
    // 检查是否有选中的作业
    if (selectedJobs.value.length === 0) {
      ElMessage.warning('请先选择要操作的作业')
      return
    }
    
    const jobNames = selectedJobs.value.map(job => job.job_name)
    
    await ElMessageBox.confirm(
      `确定要开始 ${selectedJobs.value.length} 个作业的自动恢复吗？`,
      '确认开始自动恢复',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'success'
      }
    )
    
    const response = await jobsStore.bulkStartRecovery(jobNames)
    if (response.success) {
      ElMessage.success(`已开始 ${response.data.affected_rows} 个作业的自动恢复`)
      selectedJobs.value = []  // 清空选择
      fetchJobs()
    } else {
      ElMessage.error(response.message || '开始自动恢复失败')
    }
  } catch (error) {
    if (error !== 'cancel') {
      componentLogger.error('批量开始自动恢复失败:', error)
      ElMessage.error('开始自动恢复失败: ' + (error.message || '未知错误'))
    }
  }
}

// 智能切换监控状态
const handleToggleMonitor = async () => {
  if (allMonitorActive.value) {
    // 当前大部分在监控中，执行停止监控
    await handleBulkStopMonitor()
  } else {
    // 当前大部分未监控，执行开始监控
    await handleBulkStartMonitor()
  }
}

// 智能切换恢复状态
const handleToggleRecovery = async () => {
  if (allRecoveryActive.value) {
    // 当前大部分开启了恢复，执行停止自动恢复
    await handleBulkStopRecovery()
  } else {
    // 当前大部分未开启恢复，执行开始自动恢复
    await handleBulkStartRecovery()
  }
}

// 批量关闭告警功能
const handleBulkDisableAlert = async () => {
  try {
    // 检查是否有选中的作业
    if (selectedJobs.value.length === 0) {
      ElMessage.warning('请先选择要操作的作业')
      return
    }
    
    const jobNames = selectedJobs.value.map(job => job.job_name)
    
    await ElMessageBox.confirm(
      `确定要关闭 ${selectedJobs.value.length} 个作业的告警吗？`,
      '确认关闭告警',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }
    )
    
    const response = await jobsStore.bulkDisableAlert(jobNames)
    if (response.success) {
      ElMessage.success(`已关闭 ${response.data.affected_rows} 个作业的告警`)
      selectedJobs.value = []  // 清空选择
      fetchJobs()
    } else {
      ElMessage.error(response.message || '关闭告警失败')
    }
  } catch (error) {
    if (error !== 'cancel') {
      componentLogger.error('批量关闭告警失败:', error)
      ElMessage.error('关闭告警失败: ' + (error.message || '未知错误'))
    }
  }
}

// 批量开启告警功能
const handleBulkEnableAlert = async () => {
  try {
    // 检查是否有选中的作业
    if (selectedJobs.value.length === 0) {
      ElMessage.warning('请先选择要操作的作业')
      return
    }
    
    // 过滤掉 manual_stopped=1 的作业（这些作业不能开启告警）
    const validJobs = selectedJobs.value.filter(job => job.manual_stopped !== 1)
    const invalidJobs = selectedJobs.value.filter(job => job.manual_stopped === 1)
    
    if (validJobs.length === 0) {
      ElMessage.warning('所选作业都已停止监控，无法开启告警。请先开启监控')
      return
    }
    
    if (invalidJobs.length > 0) {
      await ElMessageBox.confirm(
        `有 ${invalidJobs.length} 个作业已停止监控，将跳过这些作业。确定要开启其余 ${validJobs.length} 个作业的告警吗？`,
        '确认开启告警',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'info'
        }
      )
    } else {
      await ElMessageBox.confirm(
        `确定要开启 ${validJobs.length} 个作业的告警吗？`,
        '确认开启告警',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'success'
        }
      )
    }
    
    const jobNames = validJobs.map(job => job.job_name)
    const response = await jobsStore.bulkEnableAlert(jobNames)
    if (response.success) {
      const message = invalidJobs.length > 0 
        ? `已开启 ${response.data.affected_rows} 个作业的告警，跳过 ${invalidJobs.length} 个已停止监控的作业`
        : `已开启 ${response.data.affected_rows} 个作业的告警`
      ElMessage.success(message)
      selectedJobs.value = []  // 清空选择
      fetchJobs()
    } else {
      ElMessage.error(response.message || '开启告警失败')
    }
  } catch (error) {
    if (error !== 'cancel') {
      componentLogger.error('批量开启告警失败:', error)
      ElMessage.error('开启告警失败: ' + (error.message || '未知错误'))
    }
  }
}

// 智能切换告警状态
const handleToggleAlert = async () => {
  if (allAlertEnabled.value) {
    // 当前大部分开启了告警，执行关闭告警
    await handleBulkDisableAlert()
  } else {
    // 当前大部分未开启告警，执行开启告警
    await handleBulkEnableAlert()
  }
}

// 字段变化处理 - 使用防抖优化性能
const handleFieldChange = debounce((fields) => {
  selectedFields.value = fields
  // 保存字段选择到localStorage
  saveFieldSettings('selectedFields', fields)
  // 字段变化时重新获取数据
  fetchJobs()
}, 300)

// 获取字段组标题
const getGroupTitle = (groupKey) => {
  const titles = {
    basic: '基础信息',
    status: '状态信息',
    time: '时间信息',
    savepoint: '保存点信息',
    error: '错误信息',
    system: '系统信息'
  }
  return titles[groupKey] || groupKey
}

// 获取字段组标签类型
const getGroupTagType = (groupKey) => {
  const types = {
    basic: 'primary',
    status: 'success',
    time: 'info',
    savepoint: 'warning',
    error: 'danger',
    system: ''
  }
  return types[groupKey] || ''
}

// 获取指定状态的作业数量（优先使用全部数据的统计）
const getJobCountByStatus = (status) => {
  // 如果有全部数据的状态统计，使用它；否则使用当前筛选数据
  if (statusStats.value && Object.keys(statusStats.value).length > 0) {
    return statusStats.value[status] || 0
  }
  return jobs.value.filter(job => job.last_status === status).length
}

// 获取表格行样式类名
const getRowClassName = ({ row, rowIndex }) => {
  const status = row.last_status
  const baseClass = 'enhanced-row'
  
  switch (status) {
    case 'RUNNING':
      return `${baseClass} row-running`
    case 'FAILED':
      return `${baseClass} row-failed`
    case 'FINISHED':
      return `${baseClass} row-finished`
    case 'CANCELED':
      return `${baseClass} row-canceled`
    default:
      return `${baseClass} row-default`
  }
}

// 复制到剪贴板功能 - 使用统一工具
const handleCopyJobName = async (jobName) => {
  if (!jobName) {
    ElMessage.warning('作业名称为空，无法复制')
    return
  }
  await copyText(jobName, '作业名称', false)
  ElMessage.success({
    message: `作业名称已复制: ${jobName}`,
    duration: 2000
  })
}

const copyToClipboard = async (text, fieldName) => {
  await copyText(text, fieldName)
}

// 获取字段图标
const getFieldIcon = (fieldValue) => {
  const iconMap = {
    'job_name': 'Document',
    'job_id': 'Key',
    'job_description': 'Document',
    'jar_name': 'Box',
    'jar_id': 'Box',
    'init_jar_path': 'FolderOpened',
    'last_jar_path': 'FolderOpened',
    'entry_class': 'Cpu',
    'program_args': 'Setting',
    'last_status': 'Right',
    'auto_recovery': 'Refresh',
    'manual_stopped': 'Remove',
    'is_alert_enabled': 'Bell',
    'monitor_status': 'Monitor',
    'recovery_attempts': 'Refresh',
    'max_recovery_attempts': 'Refresh',
    'consecutive_failures': 'Warning',
    'health_status': 'CircleCheck',
    'registration_time': 'Calendar',
    'last_update_time': 'Clock',
    'last_check_time': 'Timer',
    'last_running_time': 'Right',
    'last_stop_time': 'Remove',
    'last_successful_check': 'CircleCheck',
    'last_health_check': 'CircleCheck',
    'savepoint_interval': 'Timer',
    'last_savepoint_path': 'FolderOpened',
    'last_savepoint_time': 'Calendar',
    'error_message': 'Warning',
    'last_error_type': 'Warning',
    'last_error_time': 'Calendar',
    'version': 'Tag',
    'total_failures': 'Warning'
  }
  return iconMap[fieldValue] || 'Document'
}

// 获取字段显示名称
const getFieldDisplayName = (fieldValue) => {
  const nameMap = {
    'job_name': '作业名称',
    'job_id': '作业ID',
    'job_description': '作业描述',
    'jar_name': 'JAR包',
    'jar_id': 'JAR ID',
    'init_jar_path': '初始JAR路径',
    'last_jar_path': '最新JAR路径',
    'entry_class': '入口类',
    'program_args': '程序参数',
    'last_status': '状态',
    'auto_recovery': '自动恢复',
    'manual_stopped': '是否监控',
    'is_alert_enabled': '告警开关',
    'monitor_status': '监控状态',
    'recovery_attempts': '恢复次数',
    'max_recovery_attempts': '最大恢复次数',
    'consecutive_failures': '连续失败次数',
    'health_status': '健康状态',
    'registration_time': '注册时间',
    'last_update_time': '更新时间',
    'last_check_time': '检查时间',
    'last_running_time': '运行时间',
    'last_stop_time': '停止时间',
    'last_successful_check': '成功检查',
    'last_health_check': '健康检查',
    'savepoint_interval': '保存点间隔',
    'last_savepoint_path': '保存点路径',
    'last_savepoint_time': '保存点时间',
    'error_message': '错误信息',
    'last_error_type': '错误类型',
    'last_error_time': '错误时间',
    'version': '版本号',
    'total_failures': '总失败次数'
  }
  return nameMap[fieldValue] || fieldValue
}

// 获取字段副标题
const getFieldSubtitle = (fieldValue) => {
  const subtitleMap = {
    'job_name': 'Job Name',
    'job_id': 'Job ID',
    'job_description': 'Description',
    'jar_name': 'JAR Package',
    'jar_id': 'JAR ID',
    'init_jar_path': 'Initial Path',
    'last_jar_path': 'Latest Path',
    'entry_class': 'Main Class',
    'program_args': 'Arguments',
    'last_status': 'Status',
    'auto_recovery': 'Auto Recovery',
    'manual_stopped': 'Is Monitoring',
    'is_alert_enabled': 'Alert Enabled',
    'monitor_status': 'Monitor Status',
    'recovery_attempts': 'Recovery Count',
    'max_recovery_attempts': 'Max Attempts',
    'consecutive_failures': 'Consecutive Failures',
    'health_status': 'Health',
    'registration_time': 'Registration',
    'last_update_time': 'Last Update',
    'last_check_time': 'Last Check',
    'last_running_time': 'Last Running',
    'last_stop_time': 'Last Stop',
    'last_successful_check': 'Success Check',
    'last_health_check': 'Health Check',
    'savepoint_interval': 'Interval',
    'last_savepoint_path': 'Savepoint Path',
    'last_savepoint_time': 'Savepoint Time',
    'error_message': 'Error Message',
    'last_error_type': 'Error Type',
    'last_error_time': 'Error Time',
    'version': 'Version',
    'total_failures': 'Total Failures'
  }
  return subtitleMap[fieldValue] || fieldValue
}


// 上移字段
const moveFieldUp = (index) => {
  if (index > 0) {
    const field = sortedFields.value[index]
    const prevField = sortedFields.value[index - 1]
    
    if (!field || !prevField) return
    
    const newOrder = [...fieldOrder.value]
    
    // 如果当前字段不在 fieldOrder 中，先添加到末尾
    const currentIndex = newOrder.indexOf(field.value)
    if (currentIndex === -1) {
      newOrder.push(field.value)
    }
    
    // 如果前一个字段不在 fieldOrder 中，也先添加
    const prevIndex = newOrder.indexOf(prevField.value)
    if (prevIndex === -1) {
      // 找到当前字段的位置，在前一个位置插入
      const currentPos = newOrder.indexOf(field.value)
      newOrder.splice(currentPos, 0, prevField.value)
    }
    
    // 找到两个字段在 newOrder 中的实际位置
    const fieldPos = newOrder.indexOf(field.value)
    const prevPos = newOrder.indexOf(prevField.value)
    
    // 交换位置：将当前字段移到前一个字段之前
    if (fieldPos > prevPos) {
      // 当前字段在后面，需要移到前面
      newOrder.splice(fieldPos, 1)
      newOrder.splice(prevPos, 0, field.value)
    } else {
      // 当前字段在前面，需要移到前一个字段之前
      newOrder.splice(fieldPos, 1)
      const newPrevPos = newOrder.indexOf(prevField.value)
      newOrder.splice(newPrevPos, 0, field.value)
    }
    
    fieldOrder.value = newOrder
    
    // 保存字段顺序到localStorage
    saveFieldSettings('fieldOrder', newOrder)
    // 字段顺序变化时重新获取数据
    fetchJobs()
  }
}

// 下移字段
const moveFieldDown = (index) => {
  if (index < sortedFields.value.length - 1) {
    const field = sortedFields.value[index]
    const nextField = sortedFields.value[index + 1]
    
    if (!field || !nextField) return
    
    const newOrder = [...fieldOrder.value]
    
    // 如果当前字段不在 fieldOrder 中，先添加到末尾
    const currentIndex = newOrder.indexOf(field.value)
    if (currentIndex === -1) {
      newOrder.push(field.value)
    }
    
    // 如果下一个字段不在 fieldOrder 中，也先添加
    const nextIndex = newOrder.indexOf(nextField.value)
    if (nextIndex === -1) {
      // 找到当前字段的位置，在后一个位置插入
      const currentPos = newOrder.indexOf(field.value)
      newOrder.splice(currentPos + 1, 0, nextField.value)
    }
    
    // 找到两个字段在 newOrder 中的实际位置
    const fieldPos = newOrder.indexOf(field.value)
    const nextPos = newOrder.indexOf(nextField.value)
    
    // 交换位置：将当前字段移到下一个字段之后
    if (fieldPos < nextPos) {
      // 当前字段在前面，需要移到后面
      newOrder.splice(fieldPos, 1)
      newOrder.splice(nextPos, 0, field.value)
    } else {
      // 当前字段在后面，需要移到下一个字段之后
      newOrder.splice(fieldPos, 1)
      const newNextPos = newOrder.indexOf(nextField.value)
      newOrder.splice(newNextPos + 1, 0, field.value)
    }
    
    fieldOrder.value = newOrder
    
    // 保存字段顺序到localStorage
    saveFieldSettings('fieldOrder', newOrder)
    // 字段顺序变化时重新获取数据
    fetchJobs()
  }
}

// 获取字段最小宽度
// 获取字段固定宽度（部分字段使用固定宽度）
const getFieldWidth = (fieldValue) => {
  const widthMap = {
    'job_name': 350  // 作业名称固定350px
  }
  return widthMap[fieldValue] || undefined
}

const getFieldMinWidth = (fieldValue) => {
  const widthMap = {
    'job_name': undefined,  // 作业名称使用固定宽度，不使用最小宽度
    'job_id': 160,
    'job_description': 200,
    'jar_name': 180,
    'jar_id': 180,
    'init_jar_path': 300,
    'last_jar_path': 300,
    'entry_class': 200,
    'program_args': 220,
    'last_status': 140,
    'auto_recovery': 140,
    'recovery_attempts': 140,
    'max_recovery_attempts': 150,
    'monitor_status': 140,
    'consecutive_failures': 140,
    'manual_stopped': 120,
    'is_alert_enabled': 100,
    'health_status': 100,
    'registration_time': 180,
    'last_update_time': 180,
    'last_check_time': 180,
    'last_running_time': 180,
    'last_stop_time': 180,
    'last_successful_check': 180,
    'last_health_check': 180,
    'savepoint_interval': 130,
    'last_savepoint_path': 200,
    'last_savepoint_time': 180,
    'error_message': 150,
    'last_error_type': 120,
    'last_error_time': 180,
    'version': 80
  }
  return widthMap[fieldValue] || 120
}

// 获取字段是否显示溢出提示
const getFieldShowOverflow = (fieldValue) => {
  const overflowFields = [
    'job_name', 'job_id', 'job_description', 'jar_name', 'jar_id',
    'init_jar_path', 'last_jar_path', 'entry_class', 'program_args',
    'last_savepoint_path', 'error_message'
  ]
  return overflowFields.includes(fieldValue)
}


// 生命周期
onMounted(() => {
  // 从localStorage恢复用户选择的字段
  const savedFields = localStorage.getItem('jobs_selected_fields')
  if (savedFields) {
    try {
      selectedFields.value = JSON.parse(savedFields)
    } catch (error) {
      componentLogger.warn('Failed to parse saved fields:', error)
    }
  }
  
  // 从localStorage恢复字段排序
  const savedOrder = localStorage.getItem('jobs_field_order')
  if (savedOrder) {
    try {
      const order = JSON.parse(savedOrder)
      fieldOrder.value = order
    } catch (error) {
      componentLogger.warn('Failed to parse saved field order:', error)
    }
  }
  
  // 暴露调试方法到 window 对象（仅用于开发调试）
  if (import.meta.env.DEV) {
    window.jobsDebug = {
      // 显示浮动框
      showFloatingButton: () => {
        hasBackgroundTask.value = true
        backgroundTaskProgress.value = '3/10'
        backgroundTaskCompleted.value = false
        console.log('[Jobs Debug] 浮动框已显示')
      },
      // 隐藏浮动框
      hideFloatingButton: () => {
        hasBackgroundTask.value = false
        console.log('[Jobs Debug] 浮动框已隐藏')
      },
      // 标记任务完成
      completeTask: () => {
        backgroundTaskCompleted.value = true
        backgroundTaskProgress.value = '10/10'
        console.log('[Jobs Debug] 任务已标记为完成')
      },
      // 打开进度对话框
      openDialog: () => {
        showBulkProgressDialog.value = true
        console.log('[Jobs Debug] 进度对话框已打开')
      },
      // 查看当前状态
      getState: () => {
        return {
          hasBackgroundTask: hasBackgroundTask.value,
          backgroundTaskProgress: backgroundTaskProgress.value,
          backgroundTaskCompleted: backgroundTaskCompleted.value,
          showBulkProgressDialog: showBulkProgressDialog.value
        }
      }
    }
    console.log('[Jobs Debug] 调试方法已挂载到 window.jobsDebug')
    console.log('[Jobs Debug] 可用命令:')
    console.log('  - window.jobsDebug.showFloatingButton()  // 显示浮动框')
    console.log('  - window.jobsDebug.hideFloatingButton()  // 隐藏浮动框')
    console.log('  - window.jobsDebug.completeTask()        // 标记任务完成')
    console.log('  - window.jobsDebug.openDialog()          // 打开进度对话框')
    console.log('  - window.jobsDebug.getState()            // 查看当前状态')
  }
  
  fetchJobs()
})

// 打开注册对话框并自动填充模板
const openRegisterDialog = () => {
  registerForm.value = {
    job_name: 'T_Job_Kafka2SR_TDS',
    job_description: '',
    jar_name: 'yc-example-job-1.0-SNAPSHOT.jar',
    jar_base_path: '/opt/flink/userjars/flink-web-upload',
    entry_class: 'com.jcm.Example',
    program_args: '',
    auto_recovery: true,
    max_recovery_attempts: 3,
    savepoint_interval: 24,
    job_id: ''
  }
  showRegisterDialog.value = true
}
</script>

<style scoped>
.jobs-container {
  padding: 0;
}

.page-header {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 24px;
  padding: 20px 24px;
  border-radius: 12px;
  overflow: hidden;
  color: #303133;
  min-height: 120px;
  border: 1px solid #e4e7ed;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}

.header-background {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 0;
}

.bg-gradient {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  opacity: 1;
}

.bg-pattern {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: 
    radial-gradient(circle at 20% 80%, rgba(64, 158, 255, 0.05) 0%, transparent 50%),
    radial-gradient(circle at 80% 20%, rgba(64, 158, 255, 0.05) 0%, transparent 50%);
  animation: patternMove 30s ease-in-out infinite;
}

@keyframes patternMove {
  0%, 100% { transform: translateX(0) translateY(0); }
  25% { transform: translateX(-10px) translateY(-5px); }
  50% { transform: translateX(5px) translateY(-10px); }
  75% { transform: translateX(-5px) translateY(5px); }
}

.page-title {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 24px;
  flex: 1;
}

.title-icon {
  position: relative;
}

.page-header .title-icon .icon-container {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px !important;
  height: 36px !important;
  background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%) !important;
  border-radius: 8px !important;
  color: white !important;
  transition: all 0.3s ease;
  box-shadow: 0 2px 8px rgba(103, 194, 58, 0.2) !important;
}

.page-header .title-icon .icon-container:hover {
  transform: scale(1.05);
  box-shadow: 0 4px 16px rgba(103, 194, 58, 0.3) !important;
}

.page-header .title-icon .icon-glow {
  position: absolute;
  top: -4px;
  left: -4px;
  right: -4px;
  bottom: -4px;
  background: linear-gradient(45deg, rgba(103, 194, 58, 0.2), rgba(133, 206, 97, 0.3));
  border-radius: 12px;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.page-header .title-icon .icon-container:hover .icon-glow {
  opacity: 1;
}

.title-content {
  flex: 1;
}

.title-main {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 12px;
}

.title-main h1 {
  margin: 0;
  font-size: 24px;
  font-weight: 600;
  color: #303133;
  letter-spacing: -0.3px;
}

.title-badge {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background: linear-gradient(135deg, #409eff 0%, #66b3ff 100%);
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
  color: white;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.page-description {
  margin: 0 0 12px 0;
  color: #606266;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  display: flex;
  align-items: center;
  gap: 8px;
}

.title-meta {
  display: flex;
  gap: 20px;
}

.meta-item {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #909399;
  font-size: 12px;
  font-weight: 500;
}

.page-stats {
  position: relative;
  z-index: 1;
  display: flex;
  gap: 20px;
}

.stat-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: #fff;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  min-width: 120px;
  position: relative;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.stat-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.stat-card:hover {
  background: #fff;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  border-color: #409eff;
}

.stat-card:hover::before {
  opacity: 1;
}

.stat-icon {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  font-size: 16px;
  transition: all 0.3s ease;
}

.stat-card.running .stat-icon {
  background: rgba(103, 194, 58, 0.1);
  color: #67c23a;
}

.stat-card.failed .stat-icon {
  background: rgba(245, 108, 108, 0.1);
  color: #f56c6c;
}

.stat-card.finished .stat-icon {
  background: rgba(64, 158, 255, 0.1);
  color: #409eff;
}

.stat-card.total .stat-icon {
  background: rgba(144, 147, 153, 0.1);
  color: #909399;
}


.stat-content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

.stat-number {
  font-size: 20px;
  font-weight: 700;
  color: #303133;
  line-height: 1;
}

.stat-label {
  font-size: 12px;
  color: #606266;
  font-weight: 500;
}

.stat-trend {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  color: #909399;
  font-weight: 500;
}

.page-actions {
  display: flex;
  gap: 12px;
}

.search-section {
  margin-bottom: 24px;
}

.search-card {
  border-radius: 16px;
  border: none;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  transition: all 0.3s ease;
}

.search-card:hover {
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
  transform: translateY(-2px);
}

.search-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.search-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
}

.search-title .el-icon {
  color: #409eff;
}

.search-form {
  margin: 0;
}

.search-form-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 20px;
}

.search-form-fields {
  display: flex;
  gap: 16px;
  flex: 1;
}

.search-form-actions {
  display: flex;
  gap: 8px;
  align-items: center;
}

/* 按钮自定义样式 */
.search-btn {
  background: linear-gradient(135deg, #409EFF 0%, #66B3FF 100%);
  border: none;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 500;
}

.search-btn:hover {
  background: linear-gradient(135deg, #66B3FF 0%, #409EFF 100%);
  box-shadow: 0 6px 20px rgba(64, 158, 255, 0.4);
  transform: translateY(-2px);
}

.reset-btn {
  background: linear-gradient(135deg, #E6A23C 0%, #F0C78A 100%);
  border: none;
  border-radius: 8px;
  color: #fff;
  box-shadow: 0 4px 12px rgba(230, 162, 60, 0.3);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 500;
}

.reset-btn:hover {
  background: linear-gradient(135deg, #F0C78A 0%, #E6A23C 100%);
  box-shadow: 0 6px 20px rgba(230, 162, 60, 0.4);
  transform: translateY(-2px);
  color: #fff;
}

.refresh-btn {
  background: linear-gradient(135deg, #67C23A 0%, #85CE61 100%);
  border: none;
  border-radius: 8px;
  color: #fff;
  box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 500;
}

.refresh-btn:hover {
  background: linear-gradient(135deg, #85CE61 0%, #67C23A 100%);
  box-shadow: 0 6px 20px rgba(103, 194, 58, 0.4);
  transform: translateY(-2px);
  color: #fff;
}

.field-btn {
  background: linear-gradient(135deg, #909399 0%, #B1B3B8 100%);
  border: none;
  border-radius: 8px;
  color: #fff;
  box-shadow: 0 4px 12px rgba(144, 147, 153, 0.3);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 500;
}

.field-btn:hover {
  background: linear-gradient(135deg, #B1B3B8 0%, #909399 100%);
  box-shadow: 0 6px 20px rgba(144, 147, 153, 0.4);
  transform: translateY(-2px);
  color: #fff;
}

/* 操作按钮样式优化 */
.action-buttons .el-button {
  font-weight: 500;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.action-buttons .el-button:hover {
  transform: translateY(-2px) scale(1.05);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

.action-buttons .el-button:active {
  transform: translateY(0) scale(0.98);
}

.action-buttons .el-button--primary {
  background: linear-gradient(135deg, #409EFF 0%, #66B3FF 100%);
  border: none;
}

.action-buttons .el-button--success {
  background: linear-gradient(135deg, #67C23A 0%, #85CE61 100%);
  border: none;
}

.action-buttons .el-button--warning {
  background: linear-gradient(135deg, #E6A23C 0%, #F0C78A 100%);
  border: none;
}

.action-buttons .el-button--danger {
  background: linear-gradient(135deg, #F56C6C 0%, #F78989 100%);
  border: none;
}

/* 状态标签样式 */
.status-tag {
  border-radius: 6px;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  transition: all 0.2s ease;
}

.status-tag:hover {
  transform: scale(1.05);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}

.status-icon {
  font-size: 12px;
}

/* 可复制字段样式 */
.copyable,
.copyable-field {
  position: relative;
  cursor: pointer;
  transition: all 0.3s ease;
  border-radius: 6px;
  padding: 4px 8px;
  margin: -4px -8px;
}

.copyable:hover,
.copyable-field:hover {
  background: rgba(64, 158, 255, 0.1);
  transform: scale(1.02);
}

.copy-hint {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  opacity: 0;
  transition: all 0.3s ease;
  background: rgba(64, 158, 255, 0.9);
  color: white;
  padding: 4px 6px;
  border-radius: 4px;
  font-size: 12px;
  pointer-events: none;
  z-index: 10;
}

.copyable:hover .copy-hint,
.copyable-field:hover .copy-hint {
  opacity: 1;
  transform: translateY(-50%) translateX(-4px);
}

.copy-hint::before {
  content: '';
  position: absolute;
  right: -4px;
  top: 50%;
  transform: translateY(-50%);
  border: 4px solid transparent;
  border-left-color: rgba(64, 158, 255, 0.9);
}

/* 作业名称样式优化 */
.job-name {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 0;
}

/* 作业名称列样式 */
.job-name-cell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
}

.job-name-text {
  flex: 1;
  white-space: nowrap;
  font-size: 14px;
  font-weight: 400;
  color: #303133;
  line-height: 1.4;
}

.job-name-cell .copy-btn {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  padding: 0;
  opacity: 0;
  transition: all 0.3s ease;
  background: linear-gradient(135deg, #909399 0%, #B1B3B8 100%);
  border: none;
}

.job-name-cell:hover .copy-btn {
  opacity: 1;
}

.job-name-cell .copy-btn:hover {
  transform: scale(1.1);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.job-name-cell .copy-btn:active {
  transform: scale(0.95);
}

/* 旧样式保留用于兼容 */
.job-name.copyable {
  padding: 8px 32px 8px 0;
}

.job-info {
  flex: 1;
  min-width: 0;
  margin-right: 8px;
}

.job-title {
  font-size: 14px;
  font-weight: 400;
  color: #303133;
  margin-bottom: 2px;
  line-height: 1.4;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* 可复制字段的通用样式 */
.copyable-field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 24px;
  padding: 4px 32px 4px 8px;
}

.copyable-field .el-text,
.copyable-field .el-tag {
  flex: 1;
  margin: 0;
}

/* 复制提示动画 */
@keyframes copySuccess {
  0% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.1); opacity: 0.8; }
  100% { transform: scale(1); opacity: 0; }
}

.copy-success {
  animation: copySuccess 0.6s ease-out;
}

/* 状态容器样式 */
.status-container {
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
}

.status-tag {
  border-radius: 8px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border: none;
  font-size: 12px;
  letter-spacing: 0.5px;
}

.status-tag:hover {
  transform: scale(1.05);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}

.status-icon {
  font-size: 14px;
  font-weight: bold;
}

.status-text {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* 状态指示器 */
.status-indicator {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  position: relative;
  animation: pulse 2s infinite;
}

.indicator-running {
  background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
  box-shadow: 0 0 0 0 rgba(103, 194, 58, 0.7);
}

.indicator-failed {
  background: linear-gradient(135deg, #f56c6c 0%, #f78989 100%);
  box-shadow: 0 0 0 0 rgba(245, 108, 108, 0.7);
}

.indicator-finished {
  background: linear-gradient(135deg, #409eff 0%, #66b3ff 100%);
  box-shadow: 0 0 0 0 rgba(64, 158, 255, 0.7);
}

.indicator-canceled {
  background: linear-gradient(135deg, #e6a23c 0%, #f0c78a 100%);
  box-shadow: 0 0 0 0 rgba(230, 162, 60, 0.7);
}

@keyframes pulse {
  0% {
    transform: scale(0.95);
    box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
  }
  
  70% {
    transform: scale(1);
    box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
  }
  
  100% {
    transform: scale(0.95);
    box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
  }
}

.search-form .el-form-item {
  margin-bottom: 0;
}

.field-selector-panel {
  border-top: 1px solid #e4e7ed;
  padding-top: 16px;
  margin-top: 16px;
}

.field-sorter {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.sorter-header h4 {
  margin: 0 0 4px 0;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
}

.sorter-description {
  margin: 0;
  color: #909399;
  font-size: 13px;
}

.field-list-sortable {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 400px;
  overflow-y: auto;
}

.field-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  background: #fff;
  border: 1px solid #e4e7ed;
  border-radius: 6px;
  transition: all 0.3s ease;
  cursor: move;
}

.field-item:hover {
  border-color: #409EFF;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
}

.field-item.field-disabled {
  background: #f5f7fa;
  opacity: 0.6;
}

.field-drag-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  color: #c0c4cc;
  cursor: default;
}

.drag-icon {
  font-size: 14px;
  font-weight: bold;
}

.field-sort-controls {
  display: flex;
  gap: 4px;
  margin-left: 8px;
}

.field-sort-controls .el-button {
  width: 28px;
  height: 28px;
  padding: 0;
}

/* 数字控制组件样式 */
.number-control {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 4px 0;
}

.number-control .el-button {
  width: 24px;
  height: 24px;
  padding: 0;
  font-size: 14px;
  font-weight: bold;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.number-control .el-button:hover:not(:disabled) {
  transform: scale(1.1);
  transition: transform 0.2s ease;
}

.number-control .el-button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.number-value {
  min-width: 32px;
  text-align: center;
  font-weight: 600;
  color: #303133;
  font-size: 14px;
  padding: 0 4px;
}

.field-checkbox {
  flex: 1;
  margin: 0;
}

.field-label {
  margin-left: 8px;
  font-size: 13px;
  color: #606266;
}

.required-mark {
  color: #f56c6c;
  margin-left: 4px;
  font-weight: bold;
}

.field-group-tag {
  flex-shrink: 0;
}

/* 监控状态开关样式 */
.monitor-switch {
  display: flex;
  align-items: center;
  gap: 8px;
}

.monitor-text {
  font-size: 12px;
  color: #909399;
  white-space: nowrap;
}

/* 字段设置操作按钮样式 */
.field-actions {
  display: flex;
  justify-content: center;
  gap: 8px;
  padding: 16px;
  border-top: 1px solid #e4e7ed;
  background-color: #fafafa;
  margin-top: 16px;
  flex-wrap: wrap;
}

.field-actions .el-button {
  margin: 2px;
}

/* 拖拽样式 */
.ghost {
  opacity: 0.5;
  background: #f0f9ff;
  border: 2px dashed #409EFF;
}

.chosen {
  transform: scale(1.02);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
}

.drag {
  opacity: 0.8;
  transform: rotate(2deg);
}

/* 字段面板过渡动画 */
.field-panel-enter-active,
.field-panel-leave-active {
  transition: all 0.3s ease;
  overflow: hidden;
}

.field-panel-enter-from {
  opacity: 0;
  max-height: 0;
  transform: translateY(-10px);
}

.field-panel-leave-to {
  opacity: 0;
  max-height: 0;
  transform: translateY(-10px);
}

.field-panel-enter-to,
.field-panel-leave-from {
  opacity: 1;
  max-height: 1000px;
  transform: translateY(0);
}

/* 批量操作栏 - 对齐独立批量操作示例 */
.batch-actions-bar {
  margin-bottom: 20px;
  padding: 12px 16px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  animation: slideInDown 0.3s ease;
}

.batch-info-compact {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: white;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
}

.batch-info-compact .selected-icon {
  font-size: 16px;
  color: white;
}

.batch-info-compact strong {
  font-weight: 600;
  margin: 0 2px;
}

.batch-divider {
  color: white;
  font-size: 16px;
  opacity: 0.6;
  margin: 0 4px;
}

.batch-buttons-compact {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.batch-btn-compact {
  padding: 8px 16px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border: none;
  color: #fff;
}

.batch-btn-compact:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.batch-btn-compact .el-icon {
  margin-right: 4px;
  font-size: 16px;
}

.monitor-btn {
  background: linear-gradient(135deg, #38bdf8 0%, #0ea5e9 100%);
}

.recovery-btn {
  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
}

.alert-btn {
  background: linear-gradient(135deg, #f97316 0%, #fb923c 100%);
}

@keyframes slideInDown {
  from {
    opacity: 0;
    transform: translateY(-20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.table-section {
  margin-bottom: 24px;
}

.table-card {
  border-radius: 16px;
  border: none;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  transition: all 0.3s ease;
}

.table-card:hover {
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
}

.table-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.table-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
}

.table-title .el-icon {
  color: #409eff;
}

.table-header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.table-info {
  display: flex;
  align-items: center;
  gap: 8px;
}

.job-name {
  display: flex;
  align-items: center;
  gap: 8px;
}

.action-buttons {
  display: flex;
  gap: 8px;
  flex-wrap: nowrap;
  white-space: nowrap;
  justify-content: flex-start;
  align-items: center;
  min-width: 200px;
}

.table-container {
  width: 100%;
  overflow-x: visible;
  margin: 0 auto;
  border-radius: 12px;
  background: #fff;
}

/* 增强表格样式 */
.enhanced-table {
  font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
}

.table-container .el-table {
  width: 100% !important;
  border-radius: 16px;
  overflow: hidden;
  background: #fff;
}

.table-container .el-table__header {
  background: #f8f9fa;
  border-bottom: 1px solid #e4e7ed;
}

.table-container .el-table th {
  background: transparent !important;
  border-bottom: none;
  padding: 6px 12px;
  vertical-align: middle;
}

/* 确保表格头部不换行 - 参考 StarRocks Tables 实现 */
.enhanced-table .el-table__header-wrapper th {
  font-size: 14px !important;
  font-weight: 500 !important;
  color: #606266 !important;
  white-space: nowrap !important;
}

.enhanced-table :deep(.el-table__header-wrapper) th .cell {
  white-space: nowrap !important;
  overflow: visible !important;
  display: flex !important;
  align-items: center !important;
  flex-wrap: nowrap !important;
}

/* 排序图标容器不换行 */
.enhanced-table :deep(.el-table__header-wrapper) th .caret-wrapper {
  flex-shrink: 0 !important;
  margin-left: 4px !important;
}

/* 表格内容字体大小统一 */
.enhanced-table .el-table__body-wrapper td {
  font-size: 14px !important;
}

.table-container .el-table td {
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
  padding: 12px;
  font-size: 14px;
  font-weight: 400;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
}

/* 表格行样式 */
.enhanced-row {
  position: relative;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.enhanced-row::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  transition: all 0.3s ease;
}

.row-running::before {
  background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
}

.row-failed::before {
  background: linear-gradient(135deg, #f56c6c 0%, #f78989 100%);
}

.row-finished::before {
  background: linear-gradient(135deg, #409eff 0%, #66b3ff 100%);
}

.row-canceled::before {
  background: linear-gradient(135deg, #e6a23c 0%, #f0c78a 100%);
}

.row-default::before {
  background: linear-gradient(135deg, #909399 0%, #b1b3b8 100%);
}

/* 行悬停效果 */
.enhanced-row:hover {
  transform: translateX(4px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
  z-index: 1;
}

/* 状态行悬停效果 */
.row-running:hover td {
  background: linear-gradient(135deg, rgba(103, 194, 58, 0.05) 0%, rgba(133, 206, 97, 0.05) 100%) !important;
}

.row-failed:hover td {
  background: linear-gradient(135deg, rgba(245, 108, 108, 0.05) 0%, rgba(247, 137, 137, 0.05) 100%) !important;
}

.row-finished:hover td {
  background: linear-gradient(135deg, rgba(64, 158, 255, 0.05) 0%, rgba(102, 179, 255, 0.05) 100%) !important;
}

.row-canceled:hover td {
  background: linear-gradient(135deg, rgba(230, 162, 60, 0.05) 0%, rgba(240, 199, 138, 0.05) 100%) !important;
}

.row-default:hover td {
  background: linear-gradient(135deg, rgba(144, 147, 153, 0.05) 0%, rgba(177, 179, 184, 0.05) 100%) !important;
}

/* 表格滚动条样式 */
.table-container::-webkit-scrollbar {
  height: 8px;
}

.table-container::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 4px;
}

.table-container::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 4px;
  transition: background-color 0.2s ease;
}

.table-container::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}


.job-name {
  display: flex;
  align-items: center;
  gap: 8px;
}

.job-name span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pagination {
  display: flex;
  justify-content: flex-end;
  margin-top: 0;
  padding: 24px 32px;
  background: linear-gradient(135deg, #fafbfc 0%, #f8f9fa 100%);
  border-top: 1px solid rgba(228, 231, 237, 0.6);
  border-radius: 0 0 16px 16px;
  position: relative;
}

.pagination::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, rgba(64, 158, 255, 0.2) 50%, transparent 100%);
}

.enhanced-pagination {
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(10px);
  border-radius: 12px;
  padding: 16px 24px;
  box-shadow: 
    0 8px 32px rgba(0, 0, 0, 0.08),
    0 2px 8px rgba(0, 0, 0, 0.04),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.2);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.enhanced-pagination:hover {
  transform: translateY(-2px);
  box-shadow: 
    0 12px 40px rgba(0, 0, 0, 0.12),
    0 4px 16px rgba(0, 0, 0, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.95);
}

.enhanced-pagination .el-pagination__total {
  color: #4a5568;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.5px;
}

.enhanced-pagination .el-pagination__sizes .el-select .el-input__inner {
  border-radius: 8px;
  border: 1px solid rgba(220, 223, 230, 0.8);
  background: rgba(255, 255, 255, 0.9);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 500;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
}

.enhanced-pagination .el-pagination__sizes .el-select .el-input__inner:hover {
  border-color: rgba(64, 158, 255, 0.6);
  background: #fff;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.1);
}

.enhanced-pagination .el-pagination__sizes .el-select .el-input__inner:focus {
  border-color: #409eff;
  background: #fff;
  box-shadow: 
    0 0 0 3px rgba(64, 158, 255, 0.1),
    0 4px 12px rgba(64, 158, 255, 0.15);
}

.enhanced-pagination .btn-prev,
.enhanced-pagination .btn-next {
  border-radius: 8px;
  border: 1px solid rgba(220, 223, 230, 0.8);
  background: linear-gradient(135deg, #fff 0%, #fafbfc 100%);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 600;
  color: #4a5568;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
  min-width: 40px;
  height: 40px;
}

.enhanced-pagination .btn-prev:hover,
.enhanced-pagination .btn-next:hover {
  background: linear-gradient(135deg, #409eff 0%, #66b3ff 100%);
  border-color: transparent;
  color: white;
  transform: translateY(-2px);
  box-shadow: 
    0 8px 25px rgba(64, 158, 255, 0.25),
    0 3px 10px rgba(64, 158, 255, 0.15);
}

.enhanced-pagination .btn-prev:active,
.enhanced-pagination .btn-next:active {
  transform: translateY(0);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
}

.enhanced-pagination .el-pager li {
  border-radius: 8px;
  border: 1px solid rgba(220, 223, 230, 0.8);
  background: linear-gradient(135deg, #fff 0%, #fafbfc 100%);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 600;
  margin: 0 3px;
  min-width: 40px;
  height: 40px;
  color: #4a5568;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
}

.enhanced-pagination .el-pager li:hover {
  background: linear-gradient(135deg, #f0f7ff 0%, #e6f3ff 100%);
  border-color: rgba(64, 158, 255, 0.3);
  color: #409eff;
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(64, 158, 255, 0.15);
}

.enhanced-pagination .el-pager li.is-active {
  background: linear-gradient(135deg, #409eff 0%, #66b3ff 100%);
  border-color: transparent;
  color: white;
  box-shadow: 
    0 8px 25px rgba(64, 158, 255, 0.3),
    0 3px 10px rgba(64, 158, 255, 0.2);
  transform: translateY(-1px);
}

.enhanced-pagination .el-pager li:active {
  transform: translateY(0);
}

.enhanced-pagination .el-pagination__jump {
  color: #4a5568;
  font-weight: 600;
}

.enhanced-pagination .el-pagination__jump .el-input__inner {
  border-radius: 8px;
  border: 1px solid rgba(220, 223, 230, 0.8);
  background: rgba(255, 255, 255, 0.9);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 500;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
  height: 40px;
}

.enhanced-pagination .el-pagination__jump .el-input__inner:hover {
  border-color: rgba(64, 158, 255, 0.6);
  background: #fff;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.1);
}

.enhanced-pagination .el-pagination__jump .el-input__inner:focus {
  border-color: #409eff;
  background: #fff;
  box-shadow: 
    0 0 0 3px rgba(64, 158, 255, 0.1),
    0 4px 12px rgba(64, 158, 255, 0.15);
}

/* 合并的分页控制容器 */
.pagination-control-wrapper {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  background: rgba(64, 158, 255, 0.08);
  border: 1px solid rgba(64, 158, 255, 0.2);
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  user-select: none;
  font-size: 12px;
  height: 24px;
}

.pagination-control-wrapper:hover {
  background: rgba(64, 158, 255, 0.12);
  border-color: rgba(64, 158, 255, 0.3);
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
}

.page-count {
  font-size: 12px;
  font-weight: 500;
  color: #909399;
  line-height: 1;
}

.divider {
  width: 1px;
  height: 12px;
  background: rgba(64, 158, 255, 0.3);
  margin: 0 2px;
}

.pagination-info {
  font-size: 12px;
  font-weight: 500;
  color: #409eff;
  letter-spacing: 0.5px;
  min-width: 24px;
  text-align: center;
  line-height: 1;
}

.pagination-icon {
  font-size: 12px;
  color: #409eff;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.pagination-icon.expanded {
  transform: rotate(180deg);
}

/* 响应式设计 */
@media (max-width: 768px) {
  .page-header {
    flex-direction: column;
    gap: 16px;
  }
  
  .search-form-content {
    flex-direction: column;
    gap: 16px;
  }
  
  .search-form-fields {
    flex-direction: column;
    gap: 12px;
  }
  
  .search-form .el-form-item {
    width: 100%;
  }
  
  
  .field-list {
    grid-template-columns: 1fr;
  }
  
  .action-buttons {
    flex-direction: column;
  }
}

/* 批量删除对话框样式 */
.delete-warning {
  text-align: center;
  padding: 20px;
}

.delete-warning h3 {
  margin: 16px 0;
  color: #f56c6c;
}

.delete-warning p {
  margin: 8px 0;
  line-height: 1.6;
}

/* 作业注册对话框样式 */
.job-register-dialog {
  .el-dialog__header {
    background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
    border-radius: 12px 12px 0 0;
    padding: 20px 24px;
    border-bottom: 1px solid #e4e7ed;
  }
  
  .el-dialog__body {
    padding: 24px;
    background: #fff;
  }
  
  .el-dialog__footer {
    background: #f8f9fa;
    border-radius: 0 0 12px 12px;
    padding: 16px 24px;
    border-top: 1px solid #e4e7ed;
  }
}

.dialog-header {
  display: flex;
  align-items: center;
  gap: 16px;
}

.dialog-header .header-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  background: linear-gradient(135deg, #409eff 0%, #66b3ff 100%);
  border-radius: 12px;
  color: white;
  font-size: 20px;
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
}

.header-content h3 {
  margin: 0 0 4px 0;
  font-size: 18px;
  font-weight: 600;
  color: #303133;
}

.header-content p {
  margin: 0;
  font-size: 14px;
  color: #606266;
}

.job-register-form {
  max-height: 600px;
  overflow-y: auto;
}

.form-section {
  margin-bottom: 24px;
  padding: 20px;
  background: #f8f9fa;
  border-radius: 12px;
  border: 1px solid #e4e7ed;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  transition: all 0.3s ease;
}

.form-section:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.section-title {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
  padding-bottom: 12px;
  border-bottom: 2px solid #409eff;
  color: #303133;
  font-size: 16px;
  font-weight: 600;
}

.section-title .el-icon {
  margin-right: 8px;
  font-size: 18px;
  color: #409eff;
  background: rgba(64, 158, 255, 0.1);
  padding: 8px;
  border-radius: 8px;
}

.form-tip {
  margin-left: 12px;
  color: #909399;
  font-size: 12px;
  line-height: 1.4;
}

.switch-container {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}

.switch-description {
  flex: 1;
}

.description-title {
  margin: 0 0 4px 0;
  color: #303133;
  font-size: 14px;
  font-weight: 500;
}

.description-text {
  margin: 0;
  color: #606266;
  font-size: 12px;
  line-height: 1.4;
}

.number-input {
  width: 120px;
}

/* 注册对话框表单项样式优化 */
.job-register-dialog :deep(.el-form-item) {
  margin-bottom: 20px;
}

.job-register-dialog :deep(.el-form-item__label) {
  color: #303133;
  font-weight: 600;
  font-size: 14px;
}

.job-register-dialog :deep(.el-input__wrapper) {
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  transition: all 0.3s ease;
}

.job-register-dialog :deep(.el-input__wrapper:hover) {
  border-color: #409eff;
}

.job-register-dialog :deep(.el-input__wrapper.is-focus) {
  border-color: #409eff;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
}

.job-register-dialog :deep(.el-textarea__inner) {
  border-radius: 8px;
  resize: vertical;
  border: 1px solid #e4e7ed;
  transition: all 0.3s ease;
}

.job-register-dialog :deep(.el-textarea__inner:hover) {
  border-color: #409eff;
}

.job-register-dialog :deep(.el-textarea__inner:focus) {
  border-color: #409eff;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
}

.job-register-dialog :deep(.el-input-number) {
  width: 100%;
}

.job-register-dialog :deep(.el-input-number .el-input__inner) {
  text-align: left;
  border-radius: 8px;
}

.job-register-dialog :deep(.el-switch) {
  --el-switch-on-color: #409eff;
}

.job-register-dialog :deep(.el-button) {
  border-radius: 8px;
  font-weight: 500;
}

.job-register-dialog :deep(.el-button--primary) {
  background: linear-gradient(135deg, #409eff 0%, #66b3ff 100%);
  border: none;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
}

.job-register-dialog :deep(.el-button--primary:hover) {
  background: linear-gradient(135deg, #66b3ff 0%, #409eff 100%);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}

/* 滚动条样式 */
.job-register-form::-webkit-scrollbar {
  width: 6px;
}

.job-register-form::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 3px;
}

.job-register-form::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 3px;
}

.job-register-form::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}

/* 注册对话框响应式设计 */
@media (max-width: 768px) {
  .job-register-dialog {
    width: 95% !important;
    margin: 0 auto;
  }
  
  .switch-container {
    flex-direction: column;
    gap: 8px;
  }
  
  .number-input {
    width: 100%;
  }
}

.delete-warning strong {
  color: #f56c6c;
  font-weight: bold;
}

/* 后台任务浮动按钮 - 右侧边栏样式 */
.background-task-indicator {
  position: fixed;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 16px 12px;
  background: linear-gradient(180deg, rgba(103, 194, 58, 0.95) 0%, rgba(133, 206, 97, 0.95) 100%);
  border-radius: 8px 0 0 8px;
  box-shadow: -4px 0 16px rgba(103, 194, 58, 0.4);
  cursor: pointer;
  transition: all 0.3s ease;
  min-width: 60px;
  overflow: hidden;
}

.background-task-indicator:hover {
  box-shadow: -6px 0 20px rgba(103, 194, 58, 0.6);
  transform: translateY(-50%) translateX(-4px);
}

.background-task-indicator:active {
  transform: translateY(-50%) translateX(-2px);
}

/* 收起状态 */
.background-task-indicator.collapsed {
  min-width: 32px;
  padding: 12px 8px;
}

.background-task-indicator.collapsed:hover {
  transform: translateY(-50%) translateX(-6px);
}

/* 箭头图标 */
.arrow-icon {
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 18px;
  animation: pulse-arrow 2s ease-in-out infinite;
  cursor: pointer;
}

/* 展开内容包装器 */
.task-content-wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

@keyframes pulse-arrow {
  0%, 100% { 
    opacity: 1;
    transform: translateX(0);
  }
  50% { 
    opacity: 0.7;
    transform: translateX(-3px);
  }
}

.task-icon {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.25);
  color: white;
  font-size: 20px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.icon-success {
  animation: pulse 2s ease-in-out infinite;
}

.icon-loading {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}

.task-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  writing-mode: vertical-rl;
  text-orientation: upright;
}

.task-label {
  font-size: 12px;
  font-weight: 600;
  color: white;
  letter-spacing: 2px;
  line-height: 1;
}

.task-progress {
  font-size: 11px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.9);
  letter-spacing: 1px;
  line-height: 1;
}

/* 滑动淡入淡出动画 */
.slide-fade-enter-active {
  transition: all 0.4s ease;
}

.slide-fade-leave-active {
  transition: all 0.3s ease;
}

.slide-fade-enter-from {
  opacity: 0;
  transform: translateY(-50%) translateX(100%);
}

.slide-fade-leave-to {
  opacity: 0;
  transform: translateY(-50%) translateX(100%);
}

.fade-leave-to {
  opacity: 0;
  transform: translateY(20px) scale(0.8);
}
</style>
